From 43bab31f50484ba4cc1a6e0d99f8e49eedf81eda Mon Sep 17 00:00:00 2001 From: Ari Date: Sat, 27 Jul 2024 16:45:36 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20Github=20Pages=20=EB=B0=B0=ED=8F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/css/773.47cca215.css | 9 ++++++ docs/css/866.60f074fd.css | 9 ++++++ docs/css/documentation-topic.2a4050e5.css | 9 ++++++ docs/css/index.95ac671e.css | 9 ++++++ docs/css/topic.7d90b3b4.css | 9 ++++++ docs/css/tutorials-overview.ddd5b228.css | 9 ++++++ docs/data/documentation/arinote.json | 1 + .../documentation/arinote/210802_---_--.json | 1 + .../arinote/210803_for-in_-_foreach.json | 1 + .../documentation/arinote/210804_-------.json | 1 + .../arinote/210805_api_design_guidelines.json | 1 + .../arinote/210806_----_--_ios_---.json | 1 + .../arinote/210807_----_--_ios_---.json | 1 + .../documentation/arinote/210809_enum.json | 1 + .../210811_swift---_--_let---_----------.json | 1 + .../documentation/arinote/210812_---_--.json | 1 + .../documentation/arinote/210816_----.json | 1 + .../documentation/arinote/210817_----.json | 1 + .../arinote/210820_------,_----_---.json | 1 + .../arinote/210827_filemanger.json | 1 + .../documentation/arinote/210831_result.json | 1 + ...04_---------,_--------------,_git----.json | 1 + .../arinote/211005_git,-_-----_--.json | 1 + ...--,lazy,---,----,----,-----_----_----.json | 1 + .../211007_----,---,kvo,compactmap,----.json | 1 + ...toring,_-----_----_--,_naming,_reduce.json | 1 + ...1009_optional,_dictionary,_compactmap.json | 1 + ...----,_ground_rules,_daily_scrum,_----.json | 1 + ...-,_properties,_keypath,_kvc,_ios,_hig.json | 1 + ...lse,_switch,_----_----,_reset,_reflog.json | 1 + ...thread,_serial_queue,_xcode_debugging.json | 1 + ...ldb,_gcd,_thread,_serial_queue,_async.json | 1 + ...ng,_function_types_as_parameter_types.json | 1 + ...uence,_localizederror,_error_handling.json | 1 + ...kvc,_notification,_notificationcenter.json | 1 + ...otification,_alert,_segue,_storyboard.json | 1 + ...er,_navigation_controller,_life_cycle.json | 1 + ..._initialization,_mvc,_notification_--.json | 1 + ...nsion,_typecasting,_optional,_dismiss.json | 1 + ...iewcontroller,_madal,_gesture,_naming.json | 1 + ...stringconvertible,_nsbundle,_uibutton.json | 1 + ...cationcenter,_deinit,_bundle,_preview.json | 1 + .../arinote/211030_auto_layout.json | 1 + ...wifneeded,_nsobject,_uml,_auto_layout.json | 1 + ...viewcontroller,_removearrangedsubview.json | 1 + .../211103_tdd,_unit_test,_tableview.json | 1 + ...,_droplast(),_removelast(),_poplast().json | 1 + ...lection,_extension,_---_--_--,_---_--.json | 1 + ...arc,_element,_queue,_stack,_linkelist.json | 1 + ...floatingpoint,_nan,_where,_sourcetree.json | 1 + ...ure_list,_solid,_protocol,_split,_uml.json | 1 + ...finding_and_refactoring_code,_snapkit.json | 1 + ..._link,_split,_components,_import,_get.json | 1 + ...ffset,_layoutifneeded,_setneedslayout.json | 1 + ...loop,_update_cycle,_uiview,_attribute.json | 1 + ...willlayoutsubviews,_private_extension.json | 1 + .../documentation/arinote/211121_oslog.json | 1 + ...tializer,_scrollrecttovisible,_cgrect.json | 1 + ...boutlet_didset,_updateviewconstraints.json | 1 + ...staticstring,_fork--_repository_-----.json | 1 + ..._life_cycle,_cfgetretaincount,_readme.json | 1 + .../arinote/211203_tableview,_-----.json | 1 + .../211206_tableview_----,_json,_api.json | 1 + ...ontents.json,_framework,_library,_api.json | 1 + ...mutableattributedstring,_addattribute.json | 1 + ...lect-_------,_indexpathforselectedrow.json | 1 + ...oadviewifneeded,_storyboard_reference.json | 1 + ...oice_over,_fatalerror,_---_-----,_vim.json | 1 + ...actory_pattern,_visibleviewcontroller.json | 1 + .../arinote/211215_-----_------.json | 1 + ...nc,_async,_parallel,_concurrency,_gcd.json | 1 + .../documentation/arinote/211217_gcd.json | 1 + .../arinote/211220_---_----,_----.json | 1 + .../211221_collectionview,_unit_test.json | 1 + ...11222_uicollectionviewflowlayout,_pop.json | 1 + .../arinote/211223_pop,_delegate.json | 1 + ...,_autoclosure,_weak,_wildcard,_naming.json | 1 + ...esponder_chain,_touch_event,_run_loop.json | 1 + ...chain,_touch_event,_dispatchsemaphore.json | 1 + ...ce_condition,_dispatchsemaphore,_http.json | 1 + .../documentation/arinote/211229_tcp_ip.json | 1 + .../arinote/211230_http,_udid,_uuid.json | 1 + ...inout,_async,_await,_thread_sanitizer.json | 1 + ...03_uicollectionview,_http,_urlsession.json | 1 + ...ync_method_test,_---_--,_test_doubles.json | 1 + ...ck,_wwdc2018,_urlsession,_networktest.json | 1 + ...220106_uisegmentedcontrol,_----pr1---.json | 1 + ...listcell,_contentoffset,_contentinset.json | 1 + ..._ios_file_system,_multipart_form-data.json | 1 + .../documentation/arinote/220110_nscache.json | 1 + .../arinote/220113_cache,_pagination.json | 1 + ...enting_modern_collection_views_custom.json | 1 + ..._-----_--_--,_uiimagepickercontroller.json | 1 + ...ew,_touchesbegan,_recognizer,_uiimage.json | 1 + .../220120_uialertcontroller,_keychain.json | 1 + ...ndview,_uirefreshcontrol,_---,_paging.json | 1 + ...mspacing,_viewforzooming,_------,_sql.json | 1 + ...apgesturerecognizer,_uicollectionview.json | 1 + ...uinavigationbarappearance,_appearance.json | 1 + ...8_coredata,_nspredicate,_relationship.json | 1 + ...rawing_cycle,_uisplitviewcontroller--.json | 1 + ...emitem,_uisplitviewcontrollerdelegate.json | 1 + ...estresult,_array_item_to_new_position.json | 1 + ...ntationcontroller,_viewwilltransition.json | 1 + ..._uitextviewdelegate,_typingattributes.json | 1 + ...ation,_performbatchupdate,_setediting.json | 1 + ..._dropbox,_rxswift,_spm,_dispatchgroup.json | 1 + ...20218_rxswift,_rxcocoa,_rxrelay,_mvvm.json | 1 + ...calization,_--_----,_swiftui_tutorial.json | 1 + .../arinote/220222_uisearchcontroller.json | 1 + ...rapper,_divider,_list,_geometryreader.json | 1 + ..._wholenumbervalue,_enumerated,_reduce.json | 1 + ...220228_usernotifications,_appdelegate.json | 1 + .../arinote/220301_mvvm,_----_--.json | 1 + ...clean_architecture_mvvm,_async,_await.json | 1 + .../220304_rxswift,_observable,_subject.json | 1 + ...nce_auto_layout,_rxswift,_single,_dto.json | 1 + ...ocial_login,_tls,_---,_----,_---,_---.json | 1 + ..._rx,_changed,_coordinator,_observable.json | 1 + ...leak,_rxswift,_withunretained,_widget.json | 1 + ...7_widget,_timeline,_decorator_pattern.json | 1 + ...,_spell_checking_in_xcode,_quick_help.json | 1 + .../220321_app_distribution,_hashable.json | 1 + .../arinote/220322_flyweight,_facade.json | 1 + .../arinote/220324_-----_----.json | 1 + .../220520_urlsession,_nscache,_-_---.json | 1 + ..._api,_array,_set,_tuple,_library,_map.json | 1 + .../220613_github_actions,_ci,_cd,_---.json | 1 + ...smiss,_popviewcontroller,_memory_leak.json | 1 + ...ift,_almofire,_urlprotocol,_unit_test.json | 1 + .../220923_urlsession,_cachepolicy.json | 1 + .../230710_addsubview_referencecount.json | 1 + .../arinote/230904_keyboardlayoutguide.json | 1 + ...911_opennotificationsettingsurlstring.json | 1 + ...5_uiimage_withtintcolor_renderingmode.json | 1 + ...iedrate,_waitingtoplayatspecifiedrate.json | 1 + ...e_find_and_replace_regular_expression.json | 1 + ...ickerconfiguration,_phassetcollection.json | 1 + .../documentation/arinote/architecture.json | 1 + docs/data/documentation/arinote/cs.json | 1 + docs/data/documentation/arinote/git.json | 1 + docs/data/documentation/arinote/rx.json | 1 + docs/data/documentation/arinote/swift.json | 1 + docs/data/documentation/arinote/swiftui.json | 1 + docs/data/documentation/arinote/uikit.json | 1 + docs/data/documentation/arinote/xcode.json | 1 + docs/developer-og-twitter.jpg | Bin 0 -> 12562 bytes docs/developer-og.jpg | Bin 0 -> 12856 bytes .../arinote/210802_---_--/index.html | 1 + .../210803_for-in_-_foreach/index.html | 1 + .../arinote/210804_-------/index.html | 1 + .../210805_api_design_guidelines/index.html | 1 + .../arinote/210806_----_--_ios_---/index.html | 1 + .../arinote/210807_----_--_ios_---/index.html | 1 + .../arinote/210809_enum/index.html | 1 + .../index.html | 1 + .../arinote/210812_---_--/index.html | 1 + .../arinote/210816_----/index.html | 1 + .../arinote/210817_----/index.html | 1 + .../210820_------,_----_---/index.html | 1 + .../arinote/210827_filemanger/index.html | 1 + .../arinote/210831_result/index.html | 1 + .../index.html | 1 + .../arinote/211005_git,-_-----_--/index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../arinote/211030_auto_layout/index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../arinote/211121_oslog/index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../211203_tableview,_-----/index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../arinote/211215_-----_------/index.html | 1 + .../index.html | 1 + .../arinote/211217_gcd/index.html | 1 + .../arinote/211220_---_----,_----/index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../arinote/211223_pop,_delegate/index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../arinote/211229_tcp_ip/index.html | 1 + .../211230_http,_udid,_uuid/index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../arinote/220110_nscache/index.html | 1 + .../220113_cache,_pagination/index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../220222_uisearchcontroller/index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../arinote/220301_mvvm,_----_--/index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../220322_flyweight,_facade/index.html | 1 + .../arinote/220324_-----_----/index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../220923_urlsession,_cachepolicy/index.html | 1 + .../index.html | 1 + .../230904_keyboardlayoutguide/index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../index.html | 1 + .../arinote/architecture/index.html | 1 + docs/documentation/arinote/cs/index.html | 1 + docs/documentation/arinote/git/index.html | 1 + docs/documentation/arinote/index.html | 1 + docs/documentation/arinote/rx/index.html | 1 + docs/documentation/arinote/swift/index.html | 1 + docs/documentation/arinote/swiftui/index.html | 1 + docs/documentation/arinote/uikit/index.html | 1 + docs/documentation/arinote/xcode/index.html | 1 + docs/favicon.ico | Bin 0 -> 15406 bytes docs/favicon.svg | 11 ++++++++ docs/img/added-icon.832a5d2c.svg | 11 ++++++++ docs/img/deprecated-icon.7bf1740a.svg | 11 ++++++++ docs/img/modified-icon.efb2697d.svg | 11 ++++++++ docs/index.html | 1 + docs/index/index.json | 1 + docs/js/104.35ce41d9.js | 10 +++++++ docs/js/337.274a8ccc.js | 10 +++++++ docs/js/842.bf5508df.js | 10 +++++++ docs/js/866.f57382ad.js | 10 +++++++ docs/js/chunk-vendors.bdb7cbba.js | 26 ++++++++++++++++++ docs/js/documentation-topic.71f67a4e.js | 10 +++++++ docs/js/highlight-js-bash-js.702f0c5c.js | 10 +++++++ docs/js/highlight-js-c-js.063069d3.js | 10 +++++++ docs/js/highlight-js-cpp-js.458a9ae4.js | 10 +++++++ docs/js/highlight-js-css-js.bfc4251f.js | 10 +++++++ .../highlight-js-custom-markdown.78c9f6ed.js | 10 +++++++ docs/js/highlight-js-custom-swift.738731d1.js | 10 +++++++ docs/js/highlight-js-diff-js.4db9a783.js | 10 +++++++ docs/js/highlight-js-http-js.f78e83c2.js | 10 +++++++ docs/js/highlight-js-java-js.4fe21e94.js | 10 +++++++ .../js/highlight-js-javascript-js.dfc9d16d.js | 10 +++++++ docs/js/highlight-js-json-js.2a1856ba.js | 10 +++++++ docs/js/highlight-js-llvm-js.26121771.js | 10 +++++++ docs/js/highlight-js-markdown-js.a2f456af.js | 10 +++++++ .../js/highlight-js-objectivec-js.74dea052.js | 10 +++++++ docs/js/highlight-js-perl-js.da6eda82.js | 10 +++++++ docs/js/highlight-js-php-js.c458ffa4.js | 10 +++++++ docs/js/highlight-js-python-js.60354774.js | 10 +++++++ docs/js/highlight-js-ruby-js.7272231f.js | 10 +++++++ docs/js/highlight-js-scss-js.adcd11a2.js | 10 +++++++ docs/js/highlight-js-shell-js.0ad5b20f.js | 10 +++++++ docs/js/highlight-js-swift-js.bdd5bff5.js | 10 +++++++ docs/js/highlight-js-xml-js.0d78f903.js | 10 +++++++ docs/js/index.980c39b5.js | 9 ++++++ docs/js/topic.99467a55.js | 10 +++++++ docs/js/tutorials-overview.d4267041.js | 10 +++++++ docs/metadata.json | 1 + 327 files changed, 706 insertions(+) create mode 100644 docs/css/773.47cca215.css create mode 100644 docs/css/866.60f074fd.css create mode 100644 docs/css/documentation-topic.2a4050e5.css create mode 100644 docs/css/index.95ac671e.css create mode 100644 docs/css/topic.7d90b3b4.css create mode 100644 docs/css/tutorials-overview.ddd5b228.css create mode 100644 docs/data/documentation/arinote.json create mode 100644 docs/data/documentation/arinote/210802_---_--.json create mode 100644 docs/data/documentation/arinote/210803_for-in_-_foreach.json create mode 100644 docs/data/documentation/arinote/210804_-------.json create mode 100644 docs/data/documentation/arinote/210805_api_design_guidelines.json create mode 100644 docs/data/documentation/arinote/210806_----_--_ios_---.json create mode 100644 docs/data/documentation/arinote/210807_----_--_ios_---.json create mode 100644 docs/data/documentation/arinote/210809_enum.json create mode 100644 docs/data/documentation/arinote/210811_swift---_--_let---_----------.json create mode 100644 docs/data/documentation/arinote/210812_---_--.json create mode 100644 docs/data/documentation/arinote/210816_----.json create mode 100644 docs/data/documentation/arinote/210817_----.json create mode 100644 docs/data/documentation/arinote/210820_------,_----_---.json create mode 100644 docs/data/documentation/arinote/210827_filemanger.json create mode 100644 docs/data/documentation/arinote/210831_result.json create mode 100644 docs/data/documentation/arinote/211004_---------,_--------------,_git----.json create mode 100644 docs/data/documentation/arinote/211005_git,-_-----_--.json create mode 100644 docs/data/documentation/arinote/211006_------,lazy,---,----,----,-----_----_----.json create mode 100644 docs/data/documentation/arinote/211007_----,---,kvo,compactmap,----.json create mode 100644 docs/data/documentation/arinote/211008_destructuring,_commit,_branch,_changes_requested,_refactoring,_-----_----_--,_naming,_reduce.json create mode 100644 docs/data/documentation/arinote/211009_optional,_dictionary,_compactmap.json create mode 100644 docs/data/documentation/arinote/211011_condition,_condition-list,_flow_chart,_------------,_ground_rules,_daily_scrum,_----.json create mode 100644 docs/data/documentation/arinote/211012_----,_properties,_keypath,_kvc,_ios,_hig.json create mode 100644 docs/data/documentation/arinote/211013_naming,_if-else,_switch,_----_----,_reset,_reflog.json create mode 100644 docs/data/documentation/arinote/211014_hig,_----,_---,_thread,_serial_queue,_xcode_debugging.json create mode 100644 docs/data/documentation/arinote/211015_lldb,_gcd,_thread,_serial_queue,_async.json create mode 100644 docs/data/documentation/arinote/211018_let,_var,_tool_bar,_design_patterns,_architectures,_mvc,_dictionary,_downcasting,_function_types_as_parameter_types.json create mode 100644 docs/data/documentation/arinote/211019_nested_type,_operator,_dictionary,_sequence,_localizederror,_error_handling.json create mode 100644 docs/data/documentation/arinote/211020_kvc,_notification,_notificationcenter.json create mode 100644 docs/data/documentation/arinote/211021_initalizer,_hig,_notification,_alert,_segue,_storyboard.json create mode 100644 docs/data/documentation/arinote/211022_viewcontroller,_navigation_controller,_life_cycle.json create mode 100644 docs/data/documentation/arinote/211023_initialization,_mvc,_notification_--.json create mode 100644 docs/data/documentation/arinote/211025_uinavigationbar,_localizederror,_self,_self,_protocol,_extension,_typecasting,_optional,_dismiss.json create mode 100644 docs/data/documentation/arinote/211026_device_orientation,_navigationviewcontroller,_madal,_gesture,_naming.json create mode 100644 docs/data/documentation/arinote/211028_uistepper,_auto_layout,_prepare,_extension,_customstringconvertible,_nsbundle,_uibutton.json create mode 100644 docs/data/documentation/arinote/211029_viewcontroller,_life_cycle,_notificationcenter,_deinit,_bundle,_preview.json create mode 100644 docs/data/documentation/arinote/211030_auto_layout.json create mode 100644 docs/data/documentation/arinote/211101_kvo,_property_observer,_prepare,_loadviewifneeded,_nsobject,_uml,_auto_layout.json create mode 100644 docs/data/documentation/arinote/211102_loadviewifneeded,_adjustsfontforcontentsizecategory,_adjustsfontsizetofitwidth,_uibutton,_navigationviewcontroller,_removearrangedsubview.json create mode 100644 docs/data/documentation/arinote/211103_tdd,_unit_test,_tableview.json create mode 100644 docs/data/documentation/arinote/211104_tdd,_unit_test,_@discardableresult,_cell_style,_droplast(),_removelast(),_poplast().json create mode 100644 docs/data/documentation/arinote/211105_outlet_collection,_extension,_---_--_--,_---_--.json create mode 100644 docs/data/documentation/arinote/211108_delegate,_weak,_arc,_element,_queue,_stack,_linkelist.json create mode 100644 docs/data/documentation/arinote/211109_removeall,_attribute,_commit,_numeric,_floatingpoint,_nan,_where,_sourcetree.json create mode 100644 docs/data/documentation/arinote/211111_capture_list,_solid,_protocol,_split,_uml.json create mode 100644 docs/data/documentation/arinote/211112_delegation,_numberformatter,_finding_and_refactoring_code,_snapkit.json create mode 100644 docs/data/documentation/arinote/211115_lldb,_complie,_build,_link,_split,_components,_import,_get.json create mode 100644 docs/data/documentation/arinote/211116_scroll_view,_stack_view,_setcontentoffset,_layoutifneeded,_setneedslayout.json create mode 100644 docs/data/documentation/arinote/211118_functional_programming,_higher-order_function,_main_run_loop,_update_cycle,_uiview,_attribute.json create mode 100644 docs/data/documentation/arinote/211119_viewdidload,_localizederror,_scrollview_bar_hide,_addsubview,_ibinspectable,_cornerradius,_viewwilllayoutsubviews,_private_extension.json create mode 100644 docs/data/documentation/arinote/211121_oslog.json create mode 100644 docs/data/documentation/arinote/211122_arc,_oslog,_debugprint,_assert,_initializer,_scrollrecttovisible,_cgrect.json create mode 100644 docs/data/documentation/arinote/211123_app_life_cycle,_init(frame:),_init(coder:),_frame,_bounds,_iboutlet_didset,_updateviewconstraints.json create mode 100644 docs/data/documentation/arinote/211125_auto_layout,_view_life_cycle,_staticstring,_fork--_repository_-----.json create mode 100644 docs/data/documentation/arinote/211126_app_life_cycle,_cfgetretaincount,_readme.json create mode 100644 docs/data/documentation/arinote/211203_tableview,_-----.json create mode 100644 docs/data/documentation/arinote/211206_tableview_----,_json,_api.json create mode 100644 docs/data/documentation/arinote/211207_nsdataasset,_----,_contents.json,_framework,_library,_api.json create mode 100644 docs/data/documentation/arinote/211208_navigation_bar,_uiimageview,_nsmutableattributedstring,_addattribute.json create mode 100644 docs/data/documentation/arinote/211209_-----,_tableview,_xib,_celldidselect,_select-_------,_indexpathforselectedrow.json create mode 100644 docs/data/documentation/arinote/211210_loadviewifneeded,_storyboard_reference.json create mode 100644 docs/data/documentation/arinote/211213_accessibility,_dynamic_type,_voice_over,_fatalerror,_---_-----,_vim.json create mode 100644 docs/data/documentation/arinote/211214_static,_factory_pattern,_visibleviewcontroller.json create mode 100644 docs/data/documentation/arinote/211215_-----_------.json create mode 100644 docs/data/documentation/arinote/211216_sync,_async,_parallel,_concurrency,_gcd.json create mode 100644 docs/data/documentation/arinote/211217_gcd.json create mode 100644 docs/data/documentation/arinote/211220_---_----,_----.json create mode 100644 docs/data/documentation/arinote/211221_collectionview,_unit_test.json create mode 100644 docs/data/documentation/arinote/211222_uicollectionviewflowlayout,_pop.json create mode 100644 docs/data/documentation/arinote/211223_pop,_delegate.json create mode 100644 docs/data/documentation/arinote/211224_escapingclosure,_autoclosure,_weak,_wildcard,_naming.json create mode 100644 docs/data/documentation/arinote/211226_associated_type,_responder_chain,_touch_event,_run_loop.json create mode 100644 docs/data/documentation/arinote/211227_responder_chain,_touch_event,_dispatchsemaphore.json create mode 100644 docs/data/documentation/arinote/211228_race_condition,_dispatchsemaphore,_http.json create mode 100644 docs/data/documentation/arinote/211229_tcp_ip.json create mode 100644 docs/data/documentation/arinote/211230_http,_udid,_uuid.json create mode 100644 docs/data/documentation/arinote/211231_inout,_async,_await,_thread_sanitizer.json create mode 100644 docs/data/documentation/arinote/220103_uicollectionview,_http,_urlsession.json create mode 100644 docs/data/documentation/arinote/220104_multipart,_form-data,_aync_method_test,_---_--,_test_doubles.json create mode 100644 docs/data/documentation/arinote/220105_urlprotocolmock,_wwdc2018,_urlsession,_networktest.json create mode 100644 docs/data/documentation/arinote/220106_uisegmentedcontrol,_----pr1---.json create mode 100644 docs/data/documentation/arinote/220107_tableview,_collectionviewlistcell,_contentoffset,_contentinset.json create mode 100644 docs/data/documentation/arinote/220110_filemanager,_ios_file_system,_multipart_form-data.json create mode 100644 docs/data/documentation/arinote/220110_nscache.json create mode 100644 docs/data/documentation/arinote/220113_cache,_pagination.json create mode 100644 docs/data/documentation/arinote/220114_implementing_modern_collection_views_custom.json create mode 100644 docs/data/documentation/arinote/220117_-----_--_--,_uiimagepickercontroller.json create mode 100644 docs/data/documentation/arinote/220118_uitextfield,_keyboard,_uitextview,_scrollview,_touchesbegan,_recognizer,_uiimage.json create mode 100644 docs/data/documentation/arinote/220120_uialertcontroller,_keychain.json create mode 100644 docs/data/documentation/arinote/220121_selectedbackgroundview,_uirefreshcontrol,_---,_paging.json create mode 100644 docs/data/documentation/arinote/220124_coredata,_dynamic_type,_uifontmetrics,_setcustomspacing,_viewforzooming,_------,_sql.json create mode 100644 docs/data/documentation/arinote/220125_uitapgesturerecognizer,_uicollectionview.json create mode 100644 docs/data/documentation/arinote/220127_uitextfield,_animation,_mvvm,_observable,_uinavigationbarappearance,_appearance.json create mode 100644 docs/data/documentation/arinote/220128_coredata,_nspredicate,_relationship.json create mode 100644 docs/data/documentation/arinote/220207_view_drawing_cycle,_uisplitviewcontroller--.json create mode 100644 docs/data/documentation/arinote/220208_reloadrows,_cancelstouchesinview,_translatesautoresizingmaskintoconstraints,_array_safe_access,_barbuttonsystemitem,_uisplitviewcontrollerdelegate.json create mode 100644 docs/data/documentation/arinote/220210_core_graphics,_codegen,_returnsobjectsasfaults,_nsfetchrequestresult,_array_item_to_new_position.json create mode 100644 docs/data/documentation/arinote/220211_uicontextualaction,_uialertaction,_uiactivityviewcontroller,_setselected,_selectedbackgroundview,_actionsheet,_popoverpresentationcontroller,_viewwilltransition.json create mode 100644 docs/data/documentation/arinote/220214_core_animation,_cabasicanimation,_append,_escaping,_uitableview-crash,_uitextviewdelegate,_typingattributes.json create mode 100644 docs/data/documentation/arinote/220215_dropbox,_weak_self,_escaping,_delayed_deallocation,_performbatchupdate,_setediting.json create mode 100644 docs/data/documentation/arinote/220217_dropbox,_rxswift,_spm,_dispatchgroup.json create mode 100644 docs/data/documentation/arinote/220218_rxswift,_rxcocoa,_rxrelay,_mvvm.json create mode 100644 docs/data/documentation/arinote/220221_localization,_--_----,_swiftui_tutorial.json create mode 100644 docs/data/documentation/arinote/220222_uisearchcontroller.json create mode 100644 docs/data/documentation/arinote/220224_swiftui,_property_wrapper,_divider,_list,_geometryreader.json create mode 100644 docs/data/documentation/arinote/220225_greedy,_wholenumbervalue,_enumerated,_reduce.json create mode 100644 docs/data/documentation/arinote/220228_usernotifications,_appdelegate.json create mode 100644 docs/data/documentation/arinote/220301_mvvm,_----_--.json create mode 100644 docs/data/documentation/arinote/220303_clean_architecture_mvvm,_async,_await.json create mode 100644 docs/data/documentation/arinote/220304_rxswift,_observable,_subject.json create mode 100644 docs/data/documentation/arinote/220307_high_performance_auto_layout,_rxswift,_single,_dto.json create mode 100644 docs/data/documentation/arinote/220310_oauth,_social_login,_tls,_---,_----,_---,_---.json create mode 100644 docs/data/documentation/arinote/220314_mvvm,_rx,_changed,_coordinator,_observable.json create mode 100644 docs/data/documentation/arinote/220315_memory_leak,_rxswift,_withunretained,_widget.json create mode 100644 docs/data/documentation/arinote/220317_widget,_timeline,_decorator_pattern.json create mode 100644 docs/data/documentation/arinote/220318_uistoryboard,_spell_checking_in_xcode,_quick_help.json create mode 100644 docs/data/documentation/arinote/220321_app_distribution,_hashable.json create mode 100644 docs/data/documentation/arinote/220322_flyweight,_facade.json create mode 100644 docs/data/documentation/arinote/220324_-----_----.json create mode 100644 docs/data/documentation/arinote/220520_urlsession,_nscache,_-_---.json create mode 100644 docs/data/documentation/arinote/220523_api,_rest_api,_array,_set,_tuple,_library,_map.json create mode 100644 docs/data/documentation/arinote/220613_github_actions,_ci,_cd,_---.json create mode 100644 docs/data/documentation/arinote/220711_arc,_dismiss,_popviewcontroller,_memory_leak.json create mode 100644 docs/data/documentation/arinote/220813_rxswift,_almofire,_urlprotocol,_unit_test.json create mode 100644 docs/data/documentation/arinote/220923_urlsession,_cachepolicy.json create mode 100644 docs/data/documentation/arinote/230710_addsubview_referencecount.json create mode 100644 docs/data/documentation/arinote/230904_keyboardlayoutguide.json create mode 100644 docs/data/documentation/arinote/230911_opennotificationsettingsurlstring.json create mode 100644 docs/data/documentation/arinote/230915_uiimage_withtintcolor_renderingmode.json create mode 100644 docs/data/documentation/arinote/231201_avplayer,_waitingtoplayatspecifiedrate,_waitingtoplayatspecifiedrate.json create mode 100644 docs/data/documentation/arinote/240104_userdefaults,_xcode_find_and_replace_regular_expression.json create mode 100644 docs/data/documentation/arinote/240227_photokit,_phpickerviewcontroller,_phpickerconfiguration,_phassetcollection.json create mode 100644 docs/data/documentation/arinote/architecture.json create mode 100644 docs/data/documentation/arinote/cs.json create mode 100644 docs/data/documentation/arinote/git.json create mode 100644 docs/data/documentation/arinote/rx.json create mode 100644 docs/data/documentation/arinote/swift.json create mode 100644 docs/data/documentation/arinote/swiftui.json create mode 100644 docs/data/documentation/arinote/uikit.json create mode 100644 docs/data/documentation/arinote/xcode.json create mode 100644 docs/developer-og-twitter.jpg create mode 100644 docs/developer-og.jpg create mode 100644 docs/documentation/arinote/210802_---_--/index.html create mode 100644 docs/documentation/arinote/210803_for-in_-_foreach/index.html create mode 100644 docs/documentation/arinote/210804_-------/index.html create mode 100644 docs/documentation/arinote/210805_api_design_guidelines/index.html create mode 100644 docs/documentation/arinote/210806_----_--_ios_---/index.html create mode 100644 docs/documentation/arinote/210807_----_--_ios_---/index.html create mode 100644 docs/documentation/arinote/210809_enum/index.html create mode 100644 docs/documentation/arinote/210811_swift---_--_let---_----------/index.html create mode 100644 docs/documentation/arinote/210812_---_--/index.html create mode 100644 docs/documentation/arinote/210816_----/index.html create mode 100644 docs/documentation/arinote/210817_----/index.html create mode 100644 docs/documentation/arinote/210820_------,_----_---/index.html create mode 100644 docs/documentation/arinote/210827_filemanger/index.html create mode 100644 docs/documentation/arinote/210831_result/index.html create mode 100644 docs/documentation/arinote/211004_---------,_--------------,_git----/index.html create mode 100644 docs/documentation/arinote/211005_git,-_-----_--/index.html create mode 100644 docs/documentation/arinote/211006_------,lazy,---,----,----,-----_----_----/index.html create mode 100644 docs/documentation/arinote/211007_----,---,kvo,compactmap,----/index.html create mode 100644 docs/documentation/arinote/211008_destructuring,_commit,_branch,_changes_requested,_refactoring,_-----_----_--,_naming,_reduce/index.html create mode 100644 docs/documentation/arinote/211009_optional,_dictionary,_compactmap/index.html create mode 100644 docs/documentation/arinote/211011_condition,_condition-list,_flow_chart,_------------,_ground_rules,_daily_scrum,_----/index.html create mode 100644 docs/documentation/arinote/211012_----,_properties,_keypath,_kvc,_ios,_hig/index.html create mode 100644 docs/documentation/arinote/211013_naming,_if-else,_switch,_----_----,_reset,_reflog/index.html create mode 100644 docs/documentation/arinote/211014_hig,_----,_---,_thread,_serial_queue,_xcode_debugging/index.html create mode 100644 docs/documentation/arinote/211015_lldb,_gcd,_thread,_serial_queue,_async/index.html create mode 100644 docs/documentation/arinote/211018_let,_var,_tool_bar,_design_patterns,_architectures,_mvc,_dictionary,_downcasting,_function_types_as_parameter_types/index.html create mode 100644 docs/documentation/arinote/211019_nested_type,_operator,_dictionary,_sequence,_localizederror,_error_handling/index.html create mode 100644 docs/documentation/arinote/211020_kvc,_notification,_notificationcenter/index.html create mode 100644 docs/documentation/arinote/211021_initalizer,_hig,_notification,_alert,_segue,_storyboard/index.html create mode 100644 docs/documentation/arinote/211022_viewcontroller,_navigation_controller,_life_cycle/index.html create mode 100644 docs/documentation/arinote/211023_initialization,_mvc,_notification_--/index.html create mode 100644 docs/documentation/arinote/211025_uinavigationbar,_localizederror,_self,_self,_protocol,_extension,_typecasting,_optional,_dismiss/index.html create mode 100644 docs/documentation/arinote/211026_device_orientation,_navigationviewcontroller,_madal,_gesture,_naming/index.html create mode 100644 docs/documentation/arinote/211028_uistepper,_auto_layout,_prepare,_extension,_customstringconvertible,_nsbundle,_uibutton/index.html create mode 100644 docs/documentation/arinote/211029_viewcontroller,_life_cycle,_notificationcenter,_deinit,_bundle,_preview/index.html create mode 100644 docs/documentation/arinote/211030_auto_layout/index.html create mode 100644 docs/documentation/arinote/211101_kvo,_property_observer,_prepare,_loadviewifneeded,_nsobject,_uml,_auto_layout/index.html create mode 100644 docs/documentation/arinote/211102_loadviewifneeded,_adjustsfontforcontentsizecategory,_adjustsfontsizetofitwidth,_uibutton,_navigationviewcontroller,_removearrangedsubview/index.html create mode 100644 docs/documentation/arinote/211103_tdd,_unit_test,_tableview/index.html create mode 100644 docs/documentation/arinote/211104_tdd,_unit_test,_@discardableresult,_cell_style,_droplast(),_removelast(),_poplast()/index.html create mode 100644 docs/documentation/arinote/211105_outlet_collection,_extension,_---_--_--,_---_--/index.html create mode 100644 docs/documentation/arinote/211108_delegate,_weak,_arc,_element,_queue,_stack,_linkelist/index.html create mode 100644 docs/documentation/arinote/211109_removeall,_attribute,_commit,_numeric,_floatingpoint,_nan,_where,_sourcetree/index.html create mode 100644 docs/documentation/arinote/211111_capture_list,_solid,_protocol,_split,_uml/index.html create mode 100644 docs/documentation/arinote/211112_delegation,_numberformatter,_finding_and_refactoring_code,_snapkit/index.html create mode 100644 docs/documentation/arinote/211115_lldb,_complie,_build,_link,_split,_components,_import,_get/index.html create mode 100644 docs/documentation/arinote/211116_scroll_view,_stack_view,_setcontentoffset,_layoutifneeded,_setneedslayout/index.html create mode 100644 docs/documentation/arinote/211118_functional_programming,_higher-order_function,_main_run_loop,_update_cycle,_uiview,_attribute/index.html create mode 100644 docs/documentation/arinote/211119_viewdidload,_localizederror,_scrollview_bar_hide,_addsubview,_ibinspectable,_cornerradius,_viewwilllayoutsubviews,_private_extension/index.html create mode 100644 docs/documentation/arinote/211121_oslog/index.html create mode 100644 docs/documentation/arinote/211122_arc,_oslog,_debugprint,_assert,_initializer,_scrollrecttovisible,_cgrect/index.html create mode 100644 docs/documentation/arinote/211123_app_life_cycle,_init(frame:),_init(coder:),_frame,_bounds,_iboutlet_didset,_updateviewconstraints/index.html create mode 100644 docs/documentation/arinote/211125_auto_layout,_view_life_cycle,_staticstring,_fork--_repository_-----/index.html create mode 100644 docs/documentation/arinote/211126_app_life_cycle,_cfgetretaincount,_readme/index.html create mode 100644 docs/documentation/arinote/211203_tableview,_-----/index.html create mode 100644 docs/documentation/arinote/211206_tableview_----,_json,_api/index.html create mode 100644 docs/documentation/arinote/211207_nsdataasset,_----,_contents.json,_framework,_library,_api/index.html create mode 100644 docs/documentation/arinote/211208_navigation_bar,_uiimageview,_nsmutableattributedstring,_addattribute/index.html create mode 100644 docs/documentation/arinote/211209_-----,_tableview,_xib,_celldidselect,_select-_------,_indexpathforselectedrow/index.html create mode 100644 docs/documentation/arinote/211210_loadviewifneeded,_storyboard_reference/index.html create mode 100644 docs/documentation/arinote/211213_accessibility,_dynamic_type,_voice_over,_fatalerror,_---_-----,_vim/index.html create mode 100644 docs/documentation/arinote/211214_static,_factory_pattern,_visibleviewcontroller/index.html create mode 100644 docs/documentation/arinote/211215_-----_------/index.html create mode 100644 docs/documentation/arinote/211216_sync,_async,_parallel,_concurrency,_gcd/index.html create mode 100644 docs/documentation/arinote/211217_gcd/index.html create mode 100644 docs/documentation/arinote/211220_---_----,_----/index.html create mode 100644 docs/documentation/arinote/211221_collectionview,_unit_test/index.html create mode 100644 docs/documentation/arinote/211222_uicollectionviewflowlayout,_pop/index.html create mode 100644 docs/documentation/arinote/211223_pop,_delegate/index.html create mode 100644 docs/documentation/arinote/211224_escapingclosure,_autoclosure,_weak,_wildcard,_naming/index.html create mode 100644 docs/documentation/arinote/211226_associated_type,_responder_chain,_touch_event,_run_loop/index.html create mode 100644 docs/documentation/arinote/211227_responder_chain,_touch_event,_dispatchsemaphore/index.html create mode 100644 docs/documentation/arinote/211228_race_condition,_dispatchsemaphore,_http/index.html create mode 100644 docs/documentation/arinote/211229_tcp_ip/index.html create mode 100644 docs/documentation/arinote/211230_http,_udid,_uuid/index.html create mode 100644 docs/documentation/arinote/211231_inout,_async,_await,_thread_sanitizer/index.html create mode 100644 docs/documentation/arinote/220103_uicollectionview,_http,_urlsession/index.html create mode 100644 docs/documentation/arinote/220104_multipart,_form-data,_aync_method_test,_---_--,_test_doubles/index.html create mode 100644 docs/documentation/arinote/220105_urlprotocolmock,_wwdc2018,_urlsession,_networktest/index.html create mode 100644 docs/documentation/arinote/220106_uisegmentedcontrol,_----pr1---/index.html create mode 100644 docs/documentation/arinote/220107_tableview,_collectionviewlistcell,_contentoffset,_contentinset/index.html create mode 100644 docs/documentation/arinote/220110_filemanager,_ios_file_system,_multipart_form-data/index.html create mode 100644 docs/documentation/arinote/220110_nscache/index.html create mode 100644 docs/documentation/arinote/220113_cache,_pagination/index.html create mode 100644 docs/documentation/arinote/220114_implementing_modern_collection_views_custom/index.html create mode 100644 docs/documentation/arinote/220117_-----_--_--,_uiimagepickercontroller/index.html create mode 100644 docs/documentation/arinote/220118_uitextfield,_keyboard,_uitextview,_scrollview,_touchesbegan,_recognizer,_uiimage/index.html create mode 100644 docs/documentation/arinote/220120_uialertcontroller,_keychain/index.html create mode 100644 docs/documentation/arinote/220121_selectedbackgroundview,_uirefreshcontrol,_---,_paging/index.html create mode 100644 docs/documentation/arinote/220124_coredata,_dynamic_type,_uifontmetrics,_setcustomspacing,_viewforzooming,_------,_sql/index.html create mode 100644 docs/documentation/arinote/220125_uitapgesturerecognizer,_uicollectionview/index.html create mode 100644 docs/documentation/arinote/220127_uitextfield,_animation,_mvvm,_observable,_uinavigationbarappearance,_appearance/index.html create mode 100644 docs/documentation/arinote/220128_coredata,_nspredicate,_relationship/index.html create mode 100644 docs/documentation/arinote/220207_view_drawing_cycle,_uisplitviewcontroller--/index.html create mode 100644 docs/documentation/arinote/220208_reloadrows,_cancelstouchesinview,_translatesautoresizingmaskintoconstraints,_array_safe_access,_barbuttonsystemitem,_uisplitviewcontrollerdelegate/index.html create mode 100644 docs/documentation/arinote/220210_core_graphics,_codegen,_returnsobjectsasfaults,_nsfetchrequestresult,_array_item_to_new_position/index.html create mode 100644 docs/documentation/arinote/220211_uicontextualaction,_uialertaction,_uiactivityviewcontroller,_setselected,_selectedbackgroundview,_actionsheet,_popoverpresentationcontroller,_viewwilltransition/index.html create mode 100644 docs/documentation/arinote/220214_core_animation,_cabasicanimation,_append,_escaping,_uitableview-crash,_uitextviewdelegate,_typingattributes/index.html create mode 100644 docs/documentation/arinote/220215_dropbox,_weak_self,_escaping,_delayed_deallocation,_performbatchupdate,_setediting/index.html create mode 100644 docs/documentation/arinote/220217_dropbox,_rxswift,_spm,_dispatchgroup/index.html create mode 100644 docs/documentation/arinote/220218_rxswift,_rxcocoa,_rxrelay,_mvvm/index.html create mode 100644 docs/documentation/arinote/220221_localization,_--_----,_swiftui_tutorial/index.html create mode 100644 docs/documentation/arinote/220222_uisearchcontroller/index.html create mode 100644 docs/documentation/arinote/220224_swiftui,_property_wrapper,_divider,_list,_geometryreader/index.html create mode 100644 docs/documentation/arinote/220225_greedy,_wholenumbervalue,_enumerated,_reduce/index.html create mode 100644 docs/documentation/arinote/220228_usernotifications,_appdelegate/index.html create mode 100644 docs/documentation/arinote/220301_mvvm,_----_--/index.html create mode 100644 docs/documentation/arinote/220303_clean_architecture_mvvm,_async,_await/index.html create mode 100644 docs/documentation/arinote/220304_rxswift,_observable,_subject/index.html create mode 100644 docs/documentation/arinote/220307_high_performance_auto_layout,_rxswift,_single,_dto/index.html create mode 100644 docs/documentation/arinote/220310_oauth,_social_login,_tls,_---,_----,_---,_---/index.html create mode 100644 docs/documentation/arinote/220314_mvvm,_rx,_changed,_coordinator,_observable/index.html create mode 100644 docs/documentation/arinote/220315_memory_leak,_rxswift,_withunretained,_widget/index.html create mode 100644 docs/documentation/arinote/220317_widget,_timeline,_decorator_pattern/index.html create mode 100644 docs/documentation/arinote/220318_uistoryboard,_spell_checking_in_xcode,_quick_help/index.html create mode 100644 docs/documentation/arinote/220321_app_distribution,_hashable/index.html create mode 100644 docs/documentation/arinote/220322_flyweight,_facade/index.html create mode 100644 docs/documentation/arinote/220324_-----_----/index.html create mode 100644 docs/documentation/arinote/220520_urlsession,_nscache,_-_---/index.html create mode 100644 docs/documentation/arinote/220523_api,_rest_api,_array,_set,_tuple,_library,_map/index.html create mode 100644 docs/documentation/arinote/220613_github_actions,_ci,_cd,_---/index.html create mode 100644 docs/documentation/arinote/220711_arc,_dismiss,_popviewcontroller,_memory_leak/index.html create mode 100644 docs/documentation/arinote/220813_rxswift,_almofire,_urlprotocol,_unit_test/index.html create mode 100644 docs/documentation/arinote/220923_urlsession,_cachepolicy/index.html create mode 100644 docs/documentation/arinote/230710_addsubview_referencecount/index.html create mode 100644 docs/documentation/arinote/230904_keyboardlayoutguide/index.html create mode 100644 docs/documentation/arinote/230911_opennotificationsettingsurlstring/index.html create mode 100644 docs/documentation/arinote/230915_uiimage_withtintcolor_renderingmode/index.html create mode 100644 docs/documentation/arinote/231201_avplayer,_waitingtoplayatspecifiedrate,_waitingtoplayatspecifiedrate/index.html create mode 100644 docs/documentation/arinote/240104_userdefaults,_xcode_find_and_replace_regular_expression/index.html create mode 100644 docs/documentation/arinote/240227_photokit,_phpickerviewcontroller,_phpickerconfiguration,_phassetcollection/index.html create mode 100644 docs/documentation/arinote/architecture/index.html create mode 100644 docs/documentation/arinote/cs/index.html create mode 100644 docs/documentation/arinote/git/index.html create mode 100644 docs/documentation/arinote/index.html create mode 100644 docs/documentation/arinote/rx/index.html create mode 100644 docs/documentation/arinote/swift/index.html create mode 100644 docs/documentation/arinote/swiftui/index.html create mode 100644 docs/documentation/arinote/uikit/index.html create mode 100644 docs/documentation/arinote/xcode/index.html create mode 100644 docs/favicon.ico create mode 100644 docs/favicon.svg create mode 100644 docs/img/added-icon.832a5d2c.svg create mode 100644 docs/img/deprecated-icon.7bf1740a.svg create mode 100644 docs/img/modified-icon.efb2697d.svg create mode 100644 docs/index.html create mode 100644 docs/index/index.json create mode 100644 docs/js/104.35ce41d9.js create mode 100644 docs/js/337.274a8ccc.js create mode 100644 docs/js/842.bf5508df.js create mode 100644 docs/js/866.f57382ad.js create mode 100644 docs/js/chunk-vendors.bdb7cbba.js create mode 100644 docs/js/documentation-topic.71f67a4e.js create mode 100644 docs/js/highlight-js-bash-js.702f0c5c.js create mode 100644 docs/js/highlight-js-c-js.063069d3.js create mode 100644 docs/js/highlight-js-cpp-js.458a9ae4.js create mode 100644 docs/js/highlight-js-css-js.bfc4251f.js create mode 100644 docs/js/highlight-js-custom-markdown.78c9f6ed.js create mode 100644 docs/js/highlight-js-custom-swift.738731d1.js create mode 100644 docs/js/highlight-js-diff-js.4db9a783.js create mode 100644 docs/js/highlight-js-http-js.f78e83c2.js create mode 100644 docs/js/highlight-js-java-js.4fe21e94.js create mode 100644 docs/js/highlight-js-javascript-js.dfc9d16d.js create mode 100644 docs/js/highlight-js-json-js.2a1856ba.js create mode 100644 docs/js/highlight-js-llvm-js.26121771.js create mode 100644 docs/js/highlight-js-markdown-js.a2f456af.js create mode 100644 docs/js/highlight-js-objectivec-js.74dea052.js create mode 100644 docs/js/highlight-js-perl-js.da6eda82.js create mode 100644 docs/js/highlight-js-php-js.c458ffa4.js create mode 100644 docs/js/highlight-js-python-js.60354774.js create mode 100644 docs/js/highlight-js-ruby-js.7272231f.js create mode 100644 docs/js/highlight-js-scss-js.adcd11a2.js create mode 100644 docs/js/highlight-js-shell-js.0ad5b20f.js create mode 100644 docs/js/highlight-js-swift-js.bdd5bff5.js create mode 100644 docs/js/highlight-js-xml-js.0d78f903.js create mode 100644 docs/js/index.980c39b5.js create mode 100644 docs/js/topic.99467a55.js create mode 100644 docs/js/tutorials-overview.d4267041.js create mode 100644 docs/metadata.json diff --git a/docs/css/773.47cca215.css b/docs/css/773.47cca215.css new file mode 100644 index 0000000..0d660e7 --- /dev/null +++ b/docs/css/773.47cca215.css @@ -0,0 +1,9 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */.badge[data-v-04624022]{--badge-color:var(--color-badge-default);--badge-dark-color:var(--color-badge-dark-default);font-size:.7058823529rem;line-height:1.3333333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);display:inline-block;padding:2px 4px;white-space:nowrap;border-radius:var(--badge-border-radius,1px);border-style:var(--badge-border-style,none);border-width:var(--badge-border-width,1px);margin:auto;margin-left:5px;color:var(--colors-badge-text,var(--color-badge-text));background-color:var(--badge-color)}@media screen{[data-color-scheme=dark] .badge[data-v-04624022]{background-color:var(--badge-dark-color)}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .badge[data-v-04624022]{background-color:var(--badge-dark-color)}}.badge-deprecated[data-v-04624022]{--badge-color:var(--color-badge-deprecated);--badge-dark-color:var(--color-badge-dark-deprecated)}.badge-beta[data-v-04624022]{--badge-color:var(--color-badge-beta);--badge-dark-color:var(--color-badge-dark-beta)}[data-v-3a32ffd0] .code-listing{background:var(--background,var(--color-code-background));color:var(--text,var(--color-code-plain));border-color:var(--colors-grid,var(--color-grid));border-width:var(--code-border-width,1px);border-style:var(--code-border-style,solid)}[data-v-3a32ffd0] .code-listing pre{padding:var(--code-block-style-elements-padding)}[data-v-3a32ffd0] .code-listing pre>code{font-size:.8823529412rem;line-height:1.6666666667;font-weight:400;font-family:var(--typography-html-font-mono,Menlo,monospace)}[data-v-3a32ffd0] *+.code-listing,[data-v-3a32ffd0] *+.endpoint-example,[data-v-3a32ffd0] *+.inline-image-container,[data-v-3a32ffd0] *+aside,[data-v-3a32ffd0] *+figure,[data-v-3a32ffd0] .code-listing+*,[data-v-3a32ffd0] .endpoint-example+*,[data-v-3a32ffd0] .inline-image-container+*,[data-v-3a32ffd0] aside+*,[data-v-3a32ffd0] figure+*{margin-top:var(--spacing-stacked-margin-xlarge)}[data-v-3a32ffd0] *+dl,[data-v-3a32ffd0] dl+*{margin-top:var(--spacing-stacked-margin-large)}[data-v-3a32ffd0] img{display:block;margin:auto;max-width:100%}[data-v-3a32ffd0] ol,[data-v-3a32ffd0] ol li:not(:first-child),[data-v-3a32ffd0] ul,[data-v-3a32ffd0] ul li:not(:first-child){margin-top:var(--spacing-stacked-margin-large)}@media only screen and (max-width:735px){[data-v-3a32ffd0] ol,[data-v-3a32ffd0] ul{margin-left:1.25rem}}[data-v-3a32ffd0] dt:not(:first-child){margin-top:var(--spacing-stacked-margin-large)}[data-v-3a32ffd0] dd{margin-left:2em}.topic-icon-wrapper[data-v-55f9d05d]{display:flex;align-items:center;justify-content:center;height:1.4705882353rem;flex:0 0 1.294rem;width:1.294rem;margin-right:1rem}.topic-icon[data-v-55f9d05d]{height:.8823529412rem;transform:scale(1);-webkit-transform:scale(1);overflow:visible}.topic-icon[data-v-55f9d05d] img{margin:0;display:block;width:100%;height:100%;-o-object-fit:contain;object-fit:contain}.topic-icon.curly-brackets-icon[data-v-55f9d05d]{height:1rem}.highlighted[data-v-798bdf9c]{background:var(--color-syntax-highlighted,mark)}.token-method[data-v-b15299ea]{font-weight:700}.token-keyword[data-v-b15299ea]{color:var(--syntax-keyword,var(--color-syntax-keywords))}.token-number[data-v-b15299ea]{color:var(--syntax-number,var(--color-syntax-numbers))}.token-string[data-v-b15299ea]{color:var(--syntax-string,var(--color-syntax-strings))}.attribute-link[data-v-b15299ea],.token-attribute[data-v-b15299ea]{color:var(--syntax-attribute,var(--color-syntax-keywords))}.token-internalParam[data-v-b15299ea]{color:var(--color-syntax-param-internal-name)}.type-identifier-link[data-v-b15299ea]{color:var(--syntax-type,var(--color-syntax-other-type-names))}.token-removed[data-v-b15299ea]{background-color:var(--color-highlight-red)}.token-added[data-v-b15299ea]{background-color:var(--color-highlight-green)}.decorator[data-v-17c84f82],.label[data-v-17c84f82]{color:var(--colors-secondary-label,var(--color-secondary-label))}.label[data-v-17c84f82]{font-size:1rem;line-height:1.4705882353;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.empty-token[data-v-17c84f82]{font-size:0}.empty-token[data-v-17c84f82]:after{content:" ";font-size:1rem}.abstract[data-v-0d9c6bcc],.link-block[data-v-0d9c6bcc] .badge{margin-left:2.294rem}.link-block .badge+.badge[data-v-0d9c6bcc]{margin-left:1rem}.link[data-v-0d9c6bcc]{display:flex}.link-block .badge[data-v-0d9c6bcc]{margin-top:.5rem}.link-block.has-inline-element[data-v-0d9c6bcc]{display:flex;align-items:flex-start;flex-flow:row wrap}.link-block.has-inline-element .badge[data-v-0d9c6bcc]{margin-left:1rem;margin-top:0}.link-block .has-adjacent-elements[data-v-0d9c6bcc]{padding-top:5px;padding-bottom:5px;display:inline-flex}.link-block[data-v-0d9c6bcc],.link[data-v-0d9c6bcc]{box-sizing:inherit}.link-block.changed[data-v-0d9c6bcc],.link.changed[data-v-0d9c6bcc]{padding-right:1rem;padding-left:2.1764705882rem;padding-top:8px;padding-bottom:8px;display:inline-flex;width:100%;box-sizing:border-box}.link-block.changed.changed[data-v-0d9c6bcc],.link.changed.changed[data-v-0d9c6bcc]{padding-right:1rem}@media only screen and (max-width:735px){.link-block.changed[data-v-0d9c6bcc],.link.changed[data-v-0d9c6bcc]{padding-left:0;padding-right:0}.link-block.changed.changed[data-v-0d9c6bcc],.link.changed.changed[data-v-0d9c6bcc]{padding-right:17px;padding-left:2.1764705882rem}.link-block.changed[data-v-0d9c6bcc],.link.changed[data-v-0d9c6bcc]{padding-left:0;padding-right:0}}.abstract .topic-required[data-v-0d9c6bcc]:not(:first-child){margin-top:4px}.topic-required[data-v-0d9c6bcc]{font-size:.8em}.deprecated[data-v-0d9c6bcc]{text-decoration:line-through} \ No newline at end of file diff --git a/docs/css/866.60f074fd.css b/docs/css/866.60f074fd.css new file mode 100644 index 0000000..1d28cc7 --- /dev/null +++ b/docs/css/866.60f074fd.css @@ -0,0 +1,9 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */aside[data-v-3ccce809]{-moz-column-break-inside:avoid;break-inside:avoid;border-radius:var(--aside-border-radius,var(--border-radius,4px));border-style:var(--aside-border-style,solid);border-width:var(--aside-border-width,0 0 0 6px);padding:.9411764706rem;text-align:start}aside .label[data-v-3ccce809]{font-size:1rem;line-height:1.5294117647;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}aside .label+[data-v-3ccce809]{margin-top:var(--spacing-stacked-margin-small)}aside.deprecated[data-v-3ccce809]{background-color:var(--color-aside-deprecated-background);border-color:var(--color-aside-deprecated-border);box-shadow:0 0 0 0 var(--color-aside-deprecated-border) inset,0 0 0 0 var(--color-aside-deprecated-border)}aside.deprecated .label[data-v-3ccce809]{color:var(--color-aside-deprecated)}aside.experiment[data-v-3ccce809]{background-color:var(--color-aside-experiment-background);border-color:var(--color-aside-experiment-border);box-shadow:0 0 0 0 var(--color-aside-experiment-border) inset,0 0 0 0 var(--color-aside-experiment-border)}aside.experiment .label[data-v-3ccce809]{color:var(--color-aside-experiment)}aside.important[data-v-3ccce809]{background-color:var(--color-aside-important-background);border-color:var(--color-aside-important-border);box-shadow:0 0 0 0 var(--color-aside-important-border) inset,0 0 0 0 var(--color-aside-important-border)}aside.important .label[data-v-3ccce809]{color:var(--color-aside-important)}aside.note[data-v-3ccce809]{background-color:var(--color-aside-note-background);border-color:var(--color-aside-note-border);box-shadow:0 0 0 0 var(--color-aside-note-border) inset,0 0 0 0 var(--color-aside-note-border)}aside.note .label[data-v-3ccce809]{color:var(--color-aside-note)}aside.tip[data-v-3ccce809]{background-color:var(--color-aside-tip-background);border-color:var(--color-aside-tip-border);box-shadow:0 0 0 0 var(--color-aside-tip-border) inset,0 0 0 0 var(--color-aside-tip-border)}aside.tip .label[data-v-3ccce809]{color:var(--color-aside-tip)}aside.warning[data-v-3ccce809]{background-color:var(--color-aside-warning-background);border-color:var(--color-aside-warning-border);box-shadow:0 0 0 0 var(--color-aside-warning-border) inset,0 0 0 0 var(--color-aside-warning-border)}aside.warning .label[data-v-3ccce809]{color:var(--color-aside-warning)}code[data-v-08295b2f]:before{content:attr(data-before-code)}code[data-v-08295b2f]:after{content:attr(data-after-code)}code[data-v-08295b2f]:after,code[data-v-08295b2f]:before{display:block;position:absolute;clip:rect(1px,1px,1px,1px);-webkit-clip-path:inset(0 0 99.9% 99.9%);clip-path:inset(0 0 99.9% 99.9%);overflow:hidden;height:1px;width:1px;padding:0;border:0}.swift-file-icon.file-icon[data-v-c01a6890]{height:1rem}.file-icon[data-v-7c381064]{position:relative;align-items:flex-end;height:24px;margin:0 .5rem 0 1rem}.filename[data-v-c8c40662]{color:var(--text,var(--colors-secondary-label,var(--color-secondary-label)));font-size:.9411764706rem;line-height:1.1875;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);margin-top:1rem}@media only screen and (max-width:735px){.filename[data-v-c8c40662]{font-size:.8235294118rem;line-height:1.4285714286;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);margin-top:0}}.filename>a[data-v-c8c40662],.filename>span[data-v-c8c40662]{display:flex;align-items:center;line-height:normal}a[data-v-c8c40662]{color:var(--url,var(--color-link))}.code-line-container[data-v-13e6923e]{display:inline-block;width:100%;box-sizing:border-box}.code-number[data-v-13e6923e]{display:inline-block;padding:0 1rem 0 8px;text-align:right;min-width:2em;color:#666;-webkit-user-select:none;-moz-user-select:none;user-select:none}.code-number[data-v-13e6923e]:before{content:attr(data-line-number)}.highlighted[data-v-13e6923e]{background:var(--line-highlight,var(--color-code-line-highlight));border-left:4px solid var(--color-code-line-highlight-border)}.highlighted .code-number[data-v-13e6923e]{padding-left:4px}pre[data-v-13e6923e]{padding:14px 0;display:flex;overflow:unset;-webkit-overflow-scrolling:touch;white-space:pre;word-wrap:normal;height:100%;-moz-tab-size:var(--code-indentationWidth,4);-o-tab-size:var(--code-indentationWidth,4);tab-size:var(--code-indentationWidth,4)}@media only screen and (max-width:735px){pre[data-v-13e6923e]{padding-top:.8235294118rem}}code[data-v-13e6923e]{white-space:pre;word-wrap:normal;flex-grow:9999}.code-listing[data-v-13e6923e],.container-general[data-v-13e6923e]{display:flex}.code-listing[data-v-13e6923e]{flex-direction:column;border-radius:var(--code-border-radius,var(--border-radius,4px));overflow:hidden;filter:blur(0)}.code-listing.single-line[data-v-13e6923e]{border-radius:var(--border-radius,4px)}.container-general[data-v-13e6923e]{overflow:auto}.container-general[data-v-13e6923e],pre[data-v-13e6923e]{flex-grow:1}.header-anchor[data-v-24fddf6a]{color:inherit;text-decoration:none;position:relative;padding-right:23px;display:inline-block}.header-anchor[data-v-24fddf6a]:after{position:absolute;clip:rect(1px,1px,1px,1px);-webkit-clip-path:inset(0 0 99.9% 99.9%);clip-path:inset(0 0 99.9% 99.9%);overflow:hidden;height:1px;width:1px;padding:0;border:0;content:attr(data-after-text)}.header-anchor .icon[data-v-24fddf6a]{position:absolute;right:0;bottom:.2em;display:none;height:16px;margin-left:7px}.header-anchor:focus .icon[data-v-24fddf6a],.header-anchor:hover .icon[data-v-24fddf6a]{display:inline}code[data-v-05f4a5b7]{speak-punctuation:code}.container-general[data-v-25a17a0e]{display:flex;flex-flow:row wrap}.container-general .code-line[data-v-25a17a0e]{flex:1 0 auto}.code-line-container[data-v-25a17a0e]{width:100%;align-items:center;display:flex;border-left:4px solid transparent;counter-increment:linenumbers;padding-right:14px}.code-number[data-v-25a17a0e]{font-size:.7058823529rem;line-height:1.5;font-weight:400;font-family:var(--typography-html-font-mono,Menlo,monospace);padding:0 1rem 0 8px;text-align:right;min-width:2.01em;-webkit-user-select:none;-moz-user-select:none;user-select:none}.code-number[data-v-25a17a0e]:before{content:counter(linenumbers)}.code-line[data-v-25a17a0e]{display:flex}pre[data-v-25a17a0e]{padding:14px 0;display:flex;flex-flow:row wrap;overflow:auto;-webkit-overflow-scrolling:touch;white-space:pre;word-wrap:normal}@media only screen and (max-width:735px){pre[data-v-25a17a0e]{padding-top:.8235294118rem}}.collapsible-code-listing[data-v-25a17a0e]{background:var(--background,var(--color-code-background));border-color:var(--colors-grid,var(--color-grid));color:var(--text,var(--color-code-plain));border-radius:var(--border-radius,4px);border-style:solid;border-width:1px;counter-reset:linenumbers;font-size:15px}.collapsible-code-listing.single-line[data-v-25a17a0e]{border-radius:var(--border-radius,4px)}.collapsible[data-v-25a17a0e]{background:var(--color-code-collapsible-background);color:var(--color-code-collapsible-text)}.collapsed[data-v-25a17a0e]:before{content:"⋯";display:inline-block;font-family:monospace;font-weight:700;height:100%;line-height:1;text-align:right;width:2.3rem}.collapsed .code-line-container[data-v-25a17a0e]{height:0;visibility:hidden}.row[data-v-be73599c]{box-sizing:border-box;display:flex;flex-flow:row wrap}.col[data-v-2ee3ad8b]{box-sizing:border-box;flex:none}.xlarge-1[data-v-2ee3ad8b]{flex-basis:8.3333333333%;max-width:8.3333333333%}.xlarge-2[data-v-2ee3ad8b]{flex-basis:16.6666666667%;max-width:16.6666666667%}.xlarge-3[data-v-2ee3ad8b]{flex-basis:25%;max-width:25%}.xlarge-4[data-v-2ee3ad8b]{flex-basis:33.3333333333%;max-width:33.3333333333%}.xlarge-5[data-v-2ee3ad8b]{flex-basis:41.6666666667%;max-width:41.6666666667%}.xlarge-6[data-v-2ee3ad8b]{flex-basis:50%;max-width:50%}.xlarge-7[data-v-2ee3ad8b]{flex-basis:58.3333333333%;max-width:58.3333333333%}.xlarge-8[data-v-2ee3ad8b]{flex-basis:66.6666666667%;max-width:66.6666666667%}.xlarge-9[data-v-2ee3ad8b]{flex-basis:75%;max-width:75%}.xlarge-10[data-v-2ee3ad8b]{flex-basis:83.3333333333%;max-width:83.3333333333%}.xlarge-11[data-v-2ee3ad8b]{flex-basis:91.6666666667%;max-width:91.6666666667%}.xlarge-12[data-v-2ee3ad8b]{flex-basis:100%;max-width:100%}.xlarge-centered[data-v-2ee3ad8b]{margin-left:auto;margin-right:auto}.xlarge-uncentered[data-v-2ee3ad8b]{margin-left:0;margin-right:0}.large-1[data-v-2ee3ad8b]{flex-basis:8.3333333333%;max-width:8.3333333333%}.large-2[data-v-2ee3ad8b]{flex-basis:16.6666666667%;max-width:16.6666666667%}.large-3[data-v-2ee3ad8b]{flex-basis:25%;max-width:25%}.large-4[data-v-2ee3ad8b]{flex-basis:33.3333333333%;max-width:33.3333333333%}.large-5[data-v-2ee3ad8b]{flex-basis:41.6666666667%;max-width:41.6666666667%}.large-6[data-v-2ee3ad8b]{flex-basis:50%;max-width:50%}.large-7[data-v-2ee3ad8b]{flex-basis:58.3333333333%;max-width:58.3333333333%}.large-8[data-v-2ee3ad8b]{flex-basis:66.6666666667%;max-width:66.6666666667%}.large-9[data-v-2ee3ad8b]{flex-basis:75%;max-width:75%}.large-10[data-v-2ee3ad8b]{flex-basis:83.3333333333%;max-width:83.3333333333%}.large-11[data-v-2ee3ad8b]{flex-basis:91.6666666667%;max-width:91.6666666667%}.large-12[data-v-2ee3ad8b]{flex-basis:100%;max-width:100%}.large-centered[data-v-2ee3ad8b]{margin-left:auto;margin-right:auto}.large-uncentered[data-v-2ee3ad8b]{margin-left:0;margin-right:0}@media only screen and (max-width:1250px){.medium-1[data-v-2ee3ad8b]{flex-basis:8.3333333333%;max-width:8.3333333333%}.medium-2[data-v-2ee3ad8b]{flex-basis:16.6666666667%;max-width:16.6666666667%}.medium-3[data-v-2ee3ad8b]{flex-basis:25%;max-width:25%}.medium-4[data-v-2ee3ad8b]{flex-basis:33.3333333333%;max-width:33.3333333333%}.medium-5[data-v-2ee3ad8b]{flex-basis:41.6666666667%;max-width:41.6666666667%}.medium-6[data-v-2ee3ad8b]{flex-basis:50%;max-width:50%}.medium-7[data-v-2ee3ad8b]{flex-basis:58.3333333333%;max-width:58.3333333333%}.medium-8[data-v-2ee3ad8b]{flex-basis:66.6666666667%;max-width:66.6666666667%}.medium-9[data-v-2ee3ad8b]{flex-basis:75%;max-width:75%}.medium-10[data-v-2ee3ad8b]{flex-basis:83.3333333333%;max-width:83.3333333333%}.medium-11[data-v-2ee3ad8b]{flex-basis:91.6666666667%;max-width:91.6666666667%}.medium-12[data-v-2ee3ad8b]{flex-basis:100%;max-width:100%}.medium-centered[data-v-2ee3ad8b]{margin-left:auto;margin-right:auto}.medium-uncentered[data-v-2ee3ad8b]{margin-left:0;margin-right:0}}@media only screen and (max-width:735px){.small-1[data-v-2ee3ad8b]{flex-basis:8.3333333333%;max-width:8.3333333333%}.small-2[data-v-2ee3ad8b]{flex-basis:16.6666666667%;max-width:16.6666666667%}.small-3[data-v-2ee3ad8b]{flex-basis:25%;max-width:25%}.small-4[data-v-2ee3ad8b]{flex-basis:33.3333333333%;max-width:33.3333333333%}.small-5[data-v-2ee3ad8b]{flex-basis:41.6666666667%;max-width:41.6666666667%}.small-6[data-v-2ee3ad8b]{flex-basis:50%;max-width:50%}.small-7[data-v-2ee3ad8b]{flex-basis:58.3333333333%;max-width:58.3333333333%}.small-8[data-v-2ee3ad8b]{flex-basis:66.6666666667%;max-width:66.6666666667%}.small-9[data-v-2ee3ad8b]{flex-basis:75%;max-width:75%}.small-10[data-v-2ee3ad8b]{flex-basis:83.3333333333%;max-width:83.3333333333%}.small-11[data-v-2ee3ad8b]{flex-basis:91.6666666667%;max-width:91.6666666667%}.small-12[data-v-2ee3ad8b]{flex-basis:100%;max-width:100%}.small-centered[data-v-2ee3ad8b]{margin-left:auto;margin-right:auto}.small-uncentered[data-v-2ee3ad8b]{margin-left:0;margin-right:0}}@media only screen and (max-width:320px){.xsmall-1[data-v-2ee3ad8b]{flex-basis:8.3333333333%;max-width:8.3333333333%}.xsmall-2[data-v-2ee3ad8b]{flex-basis:16.6666666667%;max-width:16.6666666667%}.xsmall-3[data-v-2ee3ad8b]{flex-basis:25%;max-width:25%}.xsmall-4[data-v-2ee3ad8b]{flex-basis:33.3333333333%;max-width:33.3333333333%}.xsmall-5[data-v-2ee3ad8b]{flex-basis:41.6666666667%;max-width:41.6666666667%}.xsmall-6[data-v-2ee3ad8b]{flex-basis:50%;max-width:50%}.xsmall-7[data-v-2ee3ad8b]{flex-basis:58.3333333333%;max-width:58.3333333333%}.xsmall-8[data-v-2ee3ad8b]{flex-basis:66.6666666667%;max-width:66.6666666667%}.xsmall-9[data-v-2ee3ad8b]{flex-basis:75%;max-width:75%}.xsmall-10[data-v-2ee3ad8b]{flex-basis:83.3333333333%;max-width:83.3333333333%}.xsmall-11[data-v-2ee3ad8b]{flex-basis:91.6666666667%;max-width:91.6666666667%}.xsmall-12[data-v-2ee3ad8b]{flex-basis:100%;max-width:100%}.xsmall-centered[data-v-2ee3ad8b]{margin-left:auto;margin-right:auto}.xsmall-uncentered[data-v-2ee3ad8b]{margin-left:0;margin-right:0}}.tabnav[data-v-5572fe1d]{margin:0 0 1.4705882353rem 0;display:flex}.tabnav--center[data-v-5572fe1d]{justify-content:center}.tabnav--end[data-v-5572fe1d]{justify-content:flex-end}.tabnav--vertical[data-v-5572fe1d]{flex-flow:column wrap}.tabnav--vertical .tabnav-items[data-v-5572fe1d]{flex-flow:column;overflow:hidden}.tabnav--vertical[data-v-5572fe1d] .tabnav-item{padding-left:0}.tabnav--vertical[data-v-5572fe1d] .tabnav-item .tabnav-link{padding-top:8px}.tabnav-items[data-v-5572fe1d]{display:flex;margin:0;text-align:center}.tabnav-item[data-v-6aa9882a]{border-bottom:1px solid;border-color:var(--colors-tabnav-item-border-color,var(--color-tabnav-item-border-color));display:flex;list-style:none;padding-left:1.7647058824rem;margin:0;outline:none}.tabnav-item[data-v-6aa9882a]:first-child{padding-left:0}.tabnav-item[data-v-6aa9882a]:nth-child(n+1){margin:0}.tabnav-link[data-v-6aa9882a]{color:var(--colors-secondary-label,var(--color-secondary-label));font-size:.8235294118rem;line-height:1;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);padding:6px 0;margin-top:4px;margin-bottom:4px;text-align:left;text-decoration:none;display:block;position:relative;z-index:0;width:100%}.tabnav-link[data-v-6aa9882a]:hover{text-decoration:none}.tabnav-link[data-v-6aa9882a]:focus{outline-offset:-1px}.tabnav-link[data-v-6aa9882a]:after{content:"";position:absolute;bottom:-5px;left:0;width:100%;border:1px solid transparent}.tabnav-link.active[data-v-6aa9882a]{color:var(--colors-text,var(--color-text));cursor:default;z-index:10}.tabnav-link.active[data-v-6aa9882a]:after{border-bottom-color:var(--colors-text,var(--color-text))}.controls[data-v-c84e62a6]{margin-top:5px;font-size:14px;display:flex;justify-content:flex-end}.controls a[data-v-c84e62a6]{color:var(--colors-text,var(--color-text));display:flex;align-items:center}.controls .control-icon[data-v-c84e62a6]{width:1.05em;margin-right:.3em}.caption[data-v-869c6f6e]{font-size:.8235294118rem;line-height:1.5;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);margin:0 0 var(--spacing-stacked-margin-large) 0}.caption.trailing[data-v-869c6f6e]{margin:var(--spacing-stacked-margin-large) 0 0 0;text-align:center}caption.trailing[data-v-869c6f6e]{caption-side:bottom}[data-v-869c6f6e] p{display:inline-block}[data-v-bf997940] img{max-width:100%}.table-wrapper[data-v-f3322390]{overflow:auto;-webkit-overflow-scrolling:touch}*+.table-wrapper[data-v-f3322390],.table-wrapper[data-v-f3322390]+*{margin-top:var(--spacing-stacked-margin-xlarge)}table[data-v-f3322390]{border-style:hidden}[data-v-f3322390] th{font-weight:600;word-break:keep-all;-webkit-hyphens:auto;hyphens:auto}[data-v-f3322390] td,[data-v-f3322390] th{border-color:var(--color-fill-gray-tertiary);border-style:solid;border-width:var(--table-border-width,1px 1px);padding:.5882352941rem}[data-v-f3322390] td.left-cell,[data-v-f3322390] th.left-cell{text-align:left}[data-v-f3322390] td.right-cell,[data-v-f3322390] th.right-cell{text-align:right}[data-v-f3322390] td.center-cell,[data-v-f3322390] th.center-cell{text-align:center}s[data-v-7fc51673]:before{content:attr(data-before-text)}s[data-v-7fc51673]:after{content:attr(data-after-text)}s[data-v-7fc51673]:after,s[data-v-7fc51673]:before{position:absolute;clip:rect(1px,1px,1px,1px);-webkit-clip-path:inset(0 0 99.9% 99.9%);clip-path:inset(0 0 99.9% 99.9%);overflow:hidden;height:1px;width:1px;padding:0;border:0}small[data-v-77035f61]{font-size:.8235294118rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-figure-gray)}.device-frame[data-v-c2eac128]{position:relative;width:var(--frame-width);aspect-ratio:var(--frame-aspect);max-width:100%;margin-left:auto;margin-right:auto;overflow:hidden}*+.device-frame[data-v-c2eac128],.device-frame[data-v-c2eac128]+*{margin-top:40px}.device[data-v-c2eac128]{background-image:var(--device-light-url);background-repeat:no-repeat;background-size:100%;width:100%;height:100%;position:relative;pointer-events:none}@media screen{[data-color-scheme=dark] .device[data-v-c2eac128]{background-image:var(--device-dark-url,var(--device-light-url))}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .device[data-v-c2eac128]{background-image:var(--device-dark-url,var(--device-light-url))}}.no-device .device[data-v-c2eac128]{display:none}.device-screen.with-device[data-v-c2eac128]{position:absolute;left:var(--screen-left);top:var(--screen-top);height:var(--screen-height);width:var(--screen-width);display:flex}.device-screen.with-device>[data-v-c2eac128]{flex:1}.device-screen.with-device[data-v-c2eac128] img{width:100%;height:100%;-o-object-fit:contain;object-fit:contain;-o-object-position:top;object-position:top;margin:0}.device-screen.with-device[data-v-c2eac128] video{-o-object-fit:contain;object-fit:contain;-o-object-position:top;object-position:top;width:100%;height:auto}.video-replay-container .control-button[data-v-3fb37a97]{display:flex;align-items:center;justify-content:center;cursor:pointer;margin-top:.5rem;-webkit-tap-highlight-color:rgba(0,0,0,0)}.video-replay-container .control-button svg.control-icon[data-v-3fb37a97]{height:12px;width:12px;margin-left:.3em}[data-v-6ab0b718] img,[data-v-6ab0b718] video{display:block;margin-left:auto;margin-right:auto;-o-object-fit:contain;object-fit:contain;max-width:100%}.asset[data-v-4f18340d]{margin-left:auto;margin-right:auto}*+.asset[data-v-4f18340d],.asset[data-v-4f18340d]+*{margin-top:var(--spacing-stacked-margin-xlarge)}[data-v-4f18340d] video{display:block;margin-left:auto;margin-right:auto;-o-object-fit:contain;object-fit:contain;max-width:100%}.column[data-v-0f654188]{grid-column:span var(--col-span);min-width:0}@media only screen and (max-width:735px){.column[data-v-0f654188]{grid-column:span 1}}.row[data-v-1bcb2d0f]{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;grid-gap:var(--col-gap,20px)}@media only screen and (max-width:735px){.row[data-v-1bcb2d0f]{grid-template-columns:1fr;grid-auto-flow:row}}.row.with-columns[data-v-1bcb2d0f]{--col-count:var(--col-count-large);grid-template-columns:repeat(var(--col-count),1fr);grid-auto-flow:row}@media only screen and (max-width:1250px){.row.with-columns[data-v-1bcb2d0f]{--col-count:var(--col-count-medium,var(--col-count-large))}}@media only screen and (max-width:735px){.row.with-columns[data-v-1bcb2d0f]{--col-count:var(--col-count-small)}}*+.TabNavigator[data-v-e671a734],*+.row[data-v-1bcb2d0f],.TabNavigator[data-v-e671a734]+*,.row[data-v-1bcb2d0f]+*{margin-top:var(--spacing-stacked-margin-xlarge)}.TabNavigator .tabnav[data-v-e671a734]{overflow:auto;white-space:nowrap}.TabNavigator .tabs-content-container[data-v-e671a734]{position:relative;overflow:hidden}.tabs--vertical[data-v-e671a734]{display:flex;flex-flow:row-reverse}@media only screen and (max-width:735px){.tabs--vertical[data-v-e671a734]{flex-flow:column-reverse}}.tabs--vertical .tabnav[data-v-e671a734]{width:30%;flex:0 0 auto;white-space:normal;margin:0}@media only screen and (max-width:735px){.tabs--vertical .tabnav[data-v-e671a734]{width:100%}}.tabs--vertical .tabs-content[data-v-e671a734]{flex:1 1 auto;min-width:0;padding-right:var(--spacing-stacked-margin-xlarge)}@media only screen and (max-width:735px){.tabs--vertical .tabs-content[data-v-e671a734]{padding-right:0;padding-bottom:var(--spacing-stacked-margin-large)}}.fade-enter-active[data-v-e671a734],.fade-leave-active[data-v-e671a734]{transition:opacity .2s ease-in-out}.fade-enter[data-v-e671a734],.fade-leave-to[data-v-e671a734]{opacity:0}.fade-leave-active[data-v-e671a734]{position:absolute;top:0;left:0;right:0}.tasklist[data-v-6a56a858]{--checkbox-width:1rem;--indent-width:calc(var(--checkbox-width)/2);--content-margin:var(--indent-width);list-style-type:none;margin-left:var(--indent-width)}p[data-v-6a56a858]{margin-left:var(--content-margin)}p[data-v-6a56a858]:only-child{--content-margin:calc(var(--checkbox-width) + var(--indent-width))}input[type=checkbox]+p[data-v-6a56a858]{display:inline-block}.pager-control[data-v-58c8390a]{align-items:center;background:var(--control-color-fill,var(--color-fill));border:1px solid var(--control-color-fill,var(--color-grid));border-radius:50%;display:flex;height:var(--control-size,1rem);justify-content:center;opacity:1;transition:opacity .15s ease-in-out;width:var(--control-size,1rem)}.pager-control[disabled][data-v-58c8390a]{opacity:.6}@media only screen and (min-width:1251px){.pager-control[disabled][data-v-58c8390a]{opacity:0}.with-compact-controls .pager-control[disabled][data-v-58c8390a]{opacity:.6}}.icon[data-v-58c8390a]{height:var(--control-icon-size,50%);width:var(--control-icon-size,50%)}.pager-control.next .icon[data-v-58c8390a]{transform:scale(1)}.pager-control.previous .icon[data-v-58c8390a]{transform:scaleX(-1)}.pager[data-v-1ed6aae0]{--control-size:3em;--control-color-fill:var(--color-fill-tertiary);--control-color-figure:currentColor;--indicator-size:0.65em;--indicator-color-fill-active:currentColor;--indicator-color-fill-inactive:var(--color-fill-tertiary);--color-svg-icon:currentColor;--gutter-width:80px}.viewport[data-v-1ed6aae0]{display:flex;overflow-x:auto;scroll-behavior:smooth;scroll-snap-type:x mandatory;scrollbar-width:none;-webkit-overflow-scrolling:touch}.viewport[data-v-1ed6aae0]::-webkit-scrollbar{height:0;width:0}.container[data-v-1ed6aae0]{position:relative}.gutter[data-v-1ed6aae0]{align-items:center;display:flex;justify-content:center;position:absolute;height:100%;top:0;width:var(--gutter-width);z-index:42}.with-compact-controls .gutter[data-v-1ed6aae0]{display:none}.gutter.left[data-v-1ed6aae0]{left:calc(var(--gutter-width)*-1)}.gutter.right[data-v-1ed6aae0]{right:calc(var(--gutter-width)*-1)}.page[data-v-1ed6aae0]{flex-shrink:0;margin-right:var(--gutter-width);position:relative;scroll-snap-align:start;transform:scale(1);transform-origin:center center;transition:transform .5s ease-in-out;-webkit-user-select:none;-moz-user-select:none;user-select:none;width:100%}@media(prefers-reduced-motion){.page[data-v-1ed6aae0]{transition:none}}.page.active[data-v-1ed6aae0]{-webkit-user-select:auto;-moz-user-select:auto;user-select:auto}.gutter .pager-control[data-v-1ed6aae0]{margin-top:calc(var(--control-size)*-1)}.indicators[data-v-1ed6aae0]{display:flex;flex-wrap:wrap;gap:1em;justify-content:center;margin-top:1rem}.with-compact-controls .indicators[data-v-1ed6aae0]{display:none}.indicator[data-v-1ed6aae0]{background:var(--indicator-color-fill-inactive);border:1px solid var(--indicator-color-fill-inactive);border-radius:50%;color:currentColor;display:block;flex:0 0 auto;height:var(--indicator-size);width:var(--indicator-size)}.indicator.active[data-v-1ed6aae0]{background:var(--indicator-color-fill-active);border-color:var(--indicator-color-fill-active)}.compact-controls[data-v-1ed6aae0]{display:none;gap:1em;justify-content:flex-end}.with-compact-controls .compact-controls[data-v-1ed6aae0]{display:flex}.button-cta[data-v-c9c81868]{background:var(--colors-button-light-background,var(--color-button-background));border-color:var(--color-button-border,currentcolor);border-radius:var(--button-border-radius,var(--border-radius,4px));border-style:var(--button-border-style,none);border-width:var(--button-border-width,medium);color:var(--colors-button-text,var(--color-button-text));cursor:pointer;min-width:1.7647058824rem;padding:.2352941176rem .8823529412rem;text-align:center;white-space:nowrap;display:inline-block;font-size:1rem;line-height:1.4705882353;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.button-cta[data-v-c9c81868]:active{background:var(--colors-button-light-backgroundActive,var(--color-button-background-active));outline:none}.button-cta[data-v-c9c81868]:hover:not([disabled]){background:var(--colors-button-light-backgroundHover,var(--color-button-background-hover));text-decoration:none}.button-cta[data-v-c9c81868]:disabled{opacity:.32;cursor:default}.fromkeyboard .button-cta[data-v-c9c81868]:focus{box-shadow:0 0 0 4px var(--color-focus-color);outline:none}.button-cta.is-dark[data-v-c9c81868]{background:var(--colors-button-dark-background,#06f)}.button-cta.is-dark[data-v-c9c81868]:active{background:var(--colors-button-dark-backgroundActive,var(--color-button-background-active))}.button-cta.is-dark[data-v-c9c81868]:hover:not([disabled]){background:var(--colors-button-dark-backgroundHover,var(--color-button-background-hover))}.card-cover-wrap.rounded[data-v-28b14a83]{border-radius:var(--border-radius,4px);overflow:hidden}.card-cover[data-v-28b14a83]{background-color:var(--color-card-background);display:block;height:var(--card-cover-height,180px)}.card-cover.fallback[data-v-28b14a83],.card-cover[data-v-28b14a83] img{width:100%;-o-object-fit:cover;object-fit:cover;-o-object-position:center;object-position:center;display:block;margin:0}.card-cover[data-v-28b14a83] img{height:100%}.card[data-v-0f7a4f31]{--margin-link:17px;overflow:hidden;display:flex;flex-direction:column;transition:box-shadow,transform .16s ease-out;will-change:box-shadow,transform;backface-visibility:hidden;border-radius:var(--border-radius,4px)}.card.large[data-v-0f7a4f31]{--margin-link:25.5px}.card.large.floating-style[data-v-0f7a4f31]{--margin-link:var(--spacing-stacked-margin-large)}.card[data-v-0f7a4f31]:hover{text-decoration:none}.card:hover .link[data-v-0f7a4f31]{text-decoration:underline}.card[data-v-0f7a4f31]:hover{transform:scale(1.007)}@media(prefers-reduced-motion:reduce){.card[data-v-0f7a4f31]:hover{transform:none}}.card.small[data-v-0f7a4f31]{--card-cover-height:235px}@media only screen and (max-width:1250px){.card.small[data-v-0f7a4f31]{--card-cover-height:163px}}.card.large[data-v-0f7a4f31]{--card-cover-height:359px}@media only screen and (max-width:1250px){.card.large[data-v-0f7a4f31]{--card-cover-height:249px}}.card.floating-style[data-v-0f7a4f31]{--color-card-shadow:transparent}.details[data-v-0f7a4f31]{flex-grow:1;display:flex;flex-direction:column;background-color:var(--color-card-background);padding:17px;position:relative;font-size:.8235294118rem;line-height:1.2857142857}.details[data-v-0f7a4f31],.large .details[data-v-0f7a4f31]{font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.large .details[data-v-0f7a4f31]{font-size:1rem;line-height:1.4705882353}@media only screen and (max-width:1250px){.large .details[data-v-0f7a4f31]{font-size:.8235294118rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.floating-style .details[data-v-0f7a4f31]{--color-card-background:transparent;padding:17px 0}.eyebrow[data-v-0f7a4f31]{color:var(--color-card-eyebrow);display:block;margin-bottom:4px;font-size:.8235294118rem;line-height:1.2857142857}.eyebrow[data-v-0f7a4f31],.large .eyebrow[data-v-0f7a4f31]{font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.large .eyebrow[data-v-0f7a4f31]{font-size:1rem;line-height:1.2352941176}@media only screen and (max-width:1250px){.large .eyebrow[data-v-0f7a4f31]{font-size:.8235294118rem;line-height:1.2857142857;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.title[data-v-0f7a4f31]{color:var(--color-card-content-text);font-size:1rem;line-height:1.2352941176;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:1250px){.title[data-v-0f7a4f31]{font-size:.8235294118rem;line-height:1.2857142857;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){.title[data-v-0f7a4f31]{font-size:1rem;line-height:1.2352941176;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.large .title[data-v-0f7a4f31]{font-size:1.2352941176rem;line-height:1.1904761905;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:1250px){.large .title[data-v-0f7a4f31]{font-size:1rem;line-height:1.2352941176;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.card-content[data-v-0f7a4f31]{flex-grow:1;color:var(--color-card-content-text);margin-top:4px}.link[data-v-0f7a4f31]{margin-top:var(--margin-link);display:flex;align-items:center}.link .link-icon[data-v-0f7a4f31]{height:.6em;width:.6em;margin-left:.3em}@media only screen and (max-width:735px){.card[data-v-0f7a4f31]{margin-left:auto;margin-right:auto}.card+.card[data-v-0f7a4f31]{margin-bottom:20px;margin-top:20px}.card.large[data-v-0f7a4f31],.card.small[data-v-0f7a4f31]{min-width:280px;--card-cover-height:227px}.card.large .link[data-v-0f7a4f31],.card.small .link[data-v-0f7a4f31]{margin-top:7px;position:relative}}.TopicTypeIcon[data-v-3b1b4787]{width:1em;height:1em;flex:0 0 auto;color:var(--icon-color,var(--color-figure-gray-secondary))}.TopicTypeIcon[data-v-3b1b4787] picture{flex:1}.TopicTypeIcon svg[data-v-3b1b4787],.TopicTypeIcon[data-v-3b1b4787] img{display:block;width:100%;height:100%}.nav-menu-items[data-v-a101abb4]{display:flex;justify-content:flex-end}.nav--in-breakpoint-range .nav-menu-items[data-v-a101abb4]{display:block;opacity:0;padding:1rem 1.8823529412rem 1.6470588235rem 1.8823529412rem;transform:translate3d(0,-50px,0);transition:transform 1s cubic-bezier(.07,1.06,.27,.95) .5s,opacity .7s cubic-bezier(.07,1.06,.27,.95) .2s}.nav--is-open.nav--in-breakpoint-range .nav-menu-items[data-v-a101abb4]{opacity:1;transform:translateZ(0);transition-delay:.2s,.4s}.nav--in-breakpoint-range .nav-menu-items[data-v-a101abb4]:not(:only-child):not(:last-child){padding-bottom:0}.nav--in-breakpoint-range .nav-menu-items[data-v-a101abb4]:not(:only-child):last-child{padding-top:0}.reference-card-grid-item[data-v-87dd3302]{--card-cover-height:auto}.reference-card-grid-item.card.large[data-v-87dd3302]{--card-cover-height:auto;min-width:0;max-width:none}.reference-card-grid-item[data-v-87dd3302] .card-cover{aspect-ratio:16/9}.reference-card-grid-item[data-v-87dd3302] .card-cover-wrap{border:1px solid var(--color-link-block-card-border)}.reference-card-grid-item__image[data-v-87dd3302]{display:flex;align-items:center;justify-content:center;font-size:80px;background-color:var(--color-fill-gray-quaternary)}.reference-card-grid-item__icon[data-v-87dd3302]{margin:0;display:flex;justify-content:center}.reference-card-grid-item__icon[data-v-87dd3302] .icon-inline{flex:1 1 auto}*+.links-block[data-v-b1a75c1c],.links-block[data-v-b1a75c1c]+*{margin-top:var(--spacing-stacked-margin-xlarge)}.topic-link-block[data-v-b1a75c1c]{margin-top:15px}.nav[data-v-40ae4336]{position:sticky;top:0;width:100%;height:3.0588235294rem;z-index:9997;--nav-padding:1.2941176471rem;color:var(--color-nav-color)}@media print{.nav[data-v-40ae4336]{position:relative}}@media only screen and (max-width:767px){.nav[data-v-40ae4336]{--nav-padding:0.9411764706rem;min-width:320px;height:2.8235294118rem}}.theme-dark.nav[data-v-40ae4336]{background:none;color:var(--color-nav-dark-color)}.nav__wrapper[data-v-40ae4336]{position:absolute;top:0;left:0;width:100%;height:auto;min-height:100%;z-index:1}.nav__background[data-v-40ae4336]{position:absolute;left:0;top:0;width:100%;height:100%;z-index:1;transition:background-color 0s ease-in;background-color:var(--color-nav-background,none)}.nav__background[data-v-40ae4336]:after{background-color:var(--color-nav-keyline)}.nav--is-sticking.nav__background[data-v-40ae4336]{background-color:none}.nav--no-bg-transition .nav__background[data-v-40ae4336]{transition:none!important}.nav--solid-background .nav__background[data-v-40ae4336]{background-color:var(--color-nav-solid-background);-webkit-backdrop-filter:none;backdrop-filter:none}.nav--is-open.nav--solid-background .nav__background[data-v-40ae4336],.nav--is-sticking.nav--solid-background .nav__background[data-v-40ae4336]{background-color:var(--color-nav-solid-background)}.nav--is-open.theme-dark.nav--solid-background .nav__background[data-v-40ae4336],.nav--is-sticking.theme-dark.nav--solid-background .nav__background[data-v-40ae4336],.theme-dark.nav--solid-background .nav__background[data-v-40ae4336]{background-color:var(--color-nav-dark-solid-background)}.nav--in-breakpoint-range .nav__background[data-v-40ae4336]{min-height:2.8235294118rem;transition:background-color 0s ease .7s}.nav--is-sticking .nav__background[data-v-40ae4336]{background-color:var(--color-nav-expanded);max-height:none;transition:background-color 0s ease;transition-property:background-color,-webkit-backdrop-filter;transition-property:background-color,backdrop-filter;transition-property:background-color,backdrop-filter,-webkit-backdrop-filter}.nav--is-sticking .nav__background[data-v-40ae4336]:after{background-color:var(--color-nav-sticking-expanded-keyline)}@supports((-webkit-backdrop-filter:initial) or (backdrop-filter:initial)){.nav--is-sticking .nav__background[data-v-40ae4336]{-webkit-backdrop-filter:saturate(180%) blur(20px);backdrop-filter:saturate(180%) blur(20px);background-color:var(--color-nav-uiblur-stuck)}}.theme-dark.nav--is-sticking .nav__background[data-v-40ae4336]{background-color:var(--color-nav-dark-stuck)}@supports((-webkit-backdrop-filter:initial) or (backdrop-filter:initial)){.theme-dark.nav--is-sticking .nav__background[data-v-40ae4336]{background-color:var(--color-nav-dark-uiblur-stuck)}}.nav--is-open .nav__background[data-v-40ae4336]{background-color:var(--color-nav-expanded);max-height:none;transition:background-color 0s ease;transition-property:background-color,-webkit-backdrop-filter;transition-property:background-color,backdrop-filter;transition-property:background-color,backdrop-filter,-webkit-backdrop-filter}.nav--is-open .nav__background[data-v-40ae4336]:after{background-color:var(--color-nav-sticking-expanded-keyline)}@supports((-webkit-backdrop-filter:initial) or (backdrop-filter:initial)){.nav--is-open .nav__background[data-v-40ae4336]{-webkit-backdrop-filter:saturate(180%) blur(20px);backdrop-filter:saturate(180%) blur(20px);background-color:var(--color-nav-uiblur-expanded)}}.theme-dark.nav--is-open .nav__background[data-v-40ae4336]{background-color:var(--color-nav-dark-expanded)}@supports((-webkit-backdrop-filter:initial) or (backdrop-filter:initial)){.theme-dark.nav--is-open .nav__background[data-v-40ae4336]{background-color:var(--color-nav-dark-uiblur-expanded)}}.theme-dark .nav__background[data-v-40ae4336]:after{background-color:var(--color-nav-dark-keyline)}.nav--is-open.theme-dark .nav__background[data-v-40ae4336]:after,.nav--is-sticking.theme-dark .nav__background[data-v-40ae4336]:after{background-color:var(--color-nav-dark-sticking-expanded-keyline)}.nav__background[data-v-40ae4336]:after{content:"";display:block;position:absolute;bottom:0;left:50%;transform:translateX(-50%);width:980px;height:1px;z-index:1}@media only screen and (max-width:1023px){.nav__background[data-v-40ae4336]:after{width:100%}}.nav--noborder .nav__background[data-v-40ae4336]:after{display:none}.nav--is-sticking.nav--noborder .nav__background[data-v-40ae4336]:after{display:block}.nav--fullwidth-border .nav__background[data-v-40ae4336]:after,.nav--is-open .nav__background[data-v-40ae4336]:after,.nav--is-sticking .nav__background[data-v-40ae4336]:after,.nav--solid-background .nav__background[data-v-40ae4336]:after{width:100%}.nav-overlay[data-v-40ae4336]{position:fixed;left:0;right:0;top:0;display:block;opacity:0}.nav--is-open .nav-overlay[data-v-40ae4336]{background-color:rgba(51,51,51,.4);transition:opacity .7s cubic-bezier(.07,1.06,.27,.95) .2s;bottom:0;opacity:1}.nav-wrapper[data-v-40ae4336]{position:absolute;top:0;left:0;width:100%;height:auto;min-height:100%;z-index:1}.pre-title[data-v-40ae4336]{display:flex}.nav-content[data-v-40ae4336]{display:flex;padding:0 var(--nav-padding);max-width:980px;margin:0 auto;position:relative;z-index:2;justify-content:space-between}.nav--is-wide-format .nav-content[data-v-40ae4336]{box-sizing:border-box;max-width:1920px;margin-left:auto;margin-right:auto}@supports(padding:calc(max(0px))){.nav-content[data-v-40ae4336]{padding-left:max(var(--nav-padding),env(safe-area-inset-left));padding-right:max(var(--nav-padding),env(safe-area-inset-right))}}.nav--in-breakpoint-range .nav-content[data-v-40ae4336]{display:grid;grid-template-columns:auto 1fr auto;grid-auto-rows:minmax(min-content,max-content);grid-template-areas:"pre-title title actions" "menu menu menu"}.nav-menu[data-v-40ae4336]{font-size:.7058823529rem;line-height:1;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);flex:1 1 auto;display:flex;justify-content:flex-end;min-width:0}@media only screen and (max-width:767px){.nav-menu[data-v-40ae4336]{font-size:.8235294118rem;line-height:1;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.nav--in-breakpoint-range .nav-menu[data-v-40ae4336]{font-size:.8235294118rem;line-height:1;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);grid-area:menu}.nav-menu-tray[data-v-40ae4336]{align-items:center;display:flex;justify-content:space-between}.nav--in-breakpoint-range .nav-menu-tray[data-v-40ae4336]{display:block;overflow:hidden;pointer-events:none;visibility:hidden;max-height:0;transition:max-height .4s ease-in 0s,visibility 0s linear 1s;width:100%}.nav--is-open.nav--in-breakpoint-range .nav-menu-tray[data-v-40ae4336]{max-height:calc(100vh - 5.64706rem);overflow-y:auto;-webkit-overflow-scrolling:touch;pointer-events:auto;visibility:visible;transition-delay:.2s,0s}.nav--is-transitioning.nav--is-open.nav--in-breakpoint-range .nav-menu-tray[data-v-40ae4336]{overflow-y:hidden}.nav--is-sticking.nav--is-open.nav--in-breakpoint-range .nav-menu-tray[data-v-40ae4336]{max-height:calc(100vh - 2.82353rem)}.nav-actions[data-v-40ae4336]{display:flex;align-items:center}.nav--in-breakpoint-range .nav-actions[data-v-40ae4336]{grid-area:actions;justify-content:flex-end}.nav--in-breakpoint-range .pre-title+.nav-title[data-v-40ae4336]{grid-area:title}.nav--is-wide-format.nav--in-breakpoint-range .pre-title+.nav-title[data-v-40ae4336]{width:100%}.nav-title[data-v-40ae4336]{height:3.0588235294rem;font-size:1.1176470588rem;line-height:1.4210526316;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);display:flex;align-items:center;white-space:nowrap;box-sizing:border-box}@media only screen and (max-width:767px){.nav-title[data-v-40ae4336]{padding-top:0;height:2.8235294118rem;width:90%}}.nav-title[data-v-40ae4336] span{line-height:normal}.nav-title a[data-v-40ae4336]{letter-spacing:inherit;line-height:normal;margin:0;text-decoration:none;white-space:nowrap}.nav-title a[data-v-40ae4336]:hover{text-decoration:none}@media only screen and (max-width:767px){.nav-title a[data-v-40ae4336]{display:flex}}.nav-title a[data-v-40ae4336],.nav-title[data-v-40ae4336]{color:var(--color-figure-gray);transition:color 0s ease-in}.nav--is-open.theme-dark .nav-title a[data-v-40ae4336],.nav--is-open.theme-dark .nav-title[data-v-40ae4336],.nav--is-sticking.theme-dark .nav-title a[data-v-40ae4336],.nav--is-sticking.theme-dark .nav-title[data-v-40ae4336],.theme-dark .nav-title a[data-v-40ae4336],.theme-dark .nav-title[data-v-40ae4336]{color:var(--color-nav-dark-link-color)}.nav-ax-toggle[data-v-40ae4336]{display:none;position:absolute;top:0;left:0;width:1px;height:1px;z-index:10}.nav-ax-toggle[data-v-40ae4336]:focus{outline-offset:-6px;width:100%;height:100%}.nav--in-breakpoint-range .nav-ax-toggle[data-v-40ae4336]{display:block}.nav-menucta[data-v-40ae4336]{cursor:pointer;display:none;align-items:center;overflow:hidden;width:1.1764705882rem;-webkit-tap-highlight-color:rgba(0,0,0,0);height:2.8235294118rem}.nav--in-breakpoint-range .nav-menucta[data-v-40ae4336]{display:flex}.nav-menucta-chevron[data-v-40ae4336]{display:block;position:relative;width:100%;height:.7058823529rem;transition:transform .3s linear}.nav-menucta-chevron[data-v-40ae4336]:after,.nav-menucta-chevron[data-v-40ae4336]:before{content:"";display:block;position:absolute;top:.5882352941rem;width:.7058823529rem;height:.0588235294rem;transition:transform .3s linear;background:var(--color-figure-gray)}.nav-menucta-chevron[data-v-40ae4336]:before{right:50%;border-radius:.5px 0 0 .5px}.nav-menucta-chevron[data-v-40ae4336]:after{left:50%;border-radius:0 .5px .5px 0}.nav-menucta-chevron[data-v-40ae4336]:before{transform-origin:100% 100%;transform:rotate(40deg) scaleY(1.5)}.nav-menucta-chevron[data-v-40ae4336]:after{transform-origin:0 100%;transform:rotate(-40deg) scaleY(1.5)}.nav--is-open .nav-menucta-chevron[data-v-40ae4336]{transform:scaleY(-1)}.theme-dark .nav-menucta-chevron[data-v-40ae4336]:after,.theme-dark .nav-menucta-chevron[data-v-40ae4336]:before{background:var(--color-nav-dark-link-color)}[data-v-40ae4336] .nav-menu-link{color:var(--color-nav-link-color)}[data-v-40ae4336] .nav-menu-link:hover{color:var(--color-nav-link-color-hover);text-decoration:none}.theme-dark[data-v-40ae4336] .nav-menu-link{color:var(--color-nav-dark-link-color)}.theme-dark[data-v-40ae4336] .nav-menu-link:hover{color:var(--color-nav-dark-link-color-hover)}[data-v-40ae4336] .nav-menu-link.current{color:var(--color-nav-current-link);cursor:default}[data-v-40ae4336] .nav-menu-link.current:hover{color:var(--color-nav-current-link)}.theme-dark[data-v-40ae4336] .nav-menu-link.current,.theme-dark[data-v-40ae4336] .nav-menu-link.current:hover{color:var(--color-nav-dark-current-link)}.nav-menu-item[data-v-296e4e0c]{margin-left:1.4117647059rem;list-style:none;min-width:0}.nav--in-breakpoint-range .nav-menu-item[data-v-296e4e0c]{margin-left:0;width:100%;min-height:2.4705882353rem}.nav--in-breakpoint-range .nav-menu-item[data-v-296e4e0c]:first-child .nav-menu-link{border-top:0}.nav--in-breakpoint-range .nav-menu-item--animated[data-v-296e4e0c]{opacity:0;transform:none;transition:.5s ease;transition-property:transform,opacity}.nav--is-open.nav--in-breakpoint-range .nav-menu-item--animated[data-v-296e4e0c]{opacity:1;transform:translateZ(0);transition-delay:0s}.thematic-break[data-v-62d2922a]{border-top-color:var(--color-grid,currentColor);border-top-style:solid;border-width:1px 0 0 0}*+.thematic-break[data-v-62d2922a],.thematic-break[data-v-62d2922a]+*{margin-top:var(--spacing-stacked-margin-xlarge)} \ No newline at end of file diff --git a/docs/css/documentation-topic.2a4050e5.css b/docs/css/documentation-topic.2a4050e5.css new file mode 100644 index 0000000..172e43d --- /dev/null +++ b/docs/css/documentation-topic.2a4050e5.css @@ -0,0 +1,9 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */.betainfo[data-v-ba3b3cc0]{font-size:.9411764706rem;padding:3rem 0;background-color:var(--color-fill-secondary)}.full-width-container .betainfo-container[data-v-ba3b3cc0]{max-width:820px;margin-left:auto;margin-right:auto;padding-left:80px;padding-right:80px;box-sizing:border-box}@media print{.full-width-container .betainfo-container[data-v-ba3b3cc0]{padding-left:20px;padding-right:20px;max-width:none}}@media only screen and (min-width:1251px){.full-width-container .betainfo-container[data-v-ba3b3cc0]{max-width:980px}}@media only screen and (min-width:1500px){.full-width-container .betainfo-container[data-v-ba3b3cc0]{max-width:1080px}}@media only screen and (max-width:735px){.full-width-container .betainfo-container[data-v-ba3b3cc0]{width:auto;padding-left:20px;padding-right:20px}}.static-width-container .betainfo-container[data-v-ba3b3cc0]{margin-left:auto;margin-right:auto;width:1536px;width:980px}@media only screen and (max-width:1250px){.static-width-container .betainfo-container[data-v-ba3b3cc0]{width:692px}}@media only screen and (max-width:735px){.static-width-container .betainfo-container[data-v-ba3b3cc0]{width:87.5%}}@media only screen and (max-width:320px){.static-width-container .betainfo-container[data-v-ba3b3cc0]{width:215px}}.betainfo-label[data-v-ba3b3cc0]{font-weight:600;font-size:.9411764706rem}.betainfo-content[data-v-ba3b3cc0] p{margin-bottom:10px}a[data-v-2ca5e993]{text-decoration:underline;color:inherit;font-weight:600}.summary-section[data-v-3aa6f694]:last-of-type{margin-right:0}@media only screen and (max-width:735px){.summary-section[data-v-3aa6f694]{margin-right:0}}.title[data-v-246c819c]{color:var(--colors-text,var(--color-text));font-size:.8235294118rem;margin-right:.5rem;text-rendering:optimizeLegibility}.language[data-v-0e39c0ba]{padding-bottom:10px;justify-content:flex-end}.language-list[data-v-0e39c0ba],.language[data-v-0e39c0ba]{font-size:.8235294118rem;line-height:1.4285714286;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);margin-top:0;display:flex;align-items:center}.language-option.swift[data-v-0e39c0ba]{padding-right:10px;border-right:1px solid var(--colors-secondary-label,var(--color-secondary-label))}.language-option.objc[data-v-0e39c0ba]{padding-left:10px}.view-more-link[data-v-3f54e653]{font-size:.8235294118rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);display:flex;flex-flow:row-reverse;margin-bottom:1.3rem}.documentation-hero[data-v-283b44ff]{color:var(--color-documentation-intro-figure,var(--color-figure-gray));overflow:hidden;text-align:left;position:relative;padding-right:var(--doc-hero-right-offset)}.documentation-hero[data-v-283b44ff]:before{content:"";background:var(--standard-accent-color,var(--color-documentation-intro-fill,var(--color-fill-tertiary)));position:absolute;width:100%;height:100%}.documentation-hero[data-v-283b44ff]:after{background:transparent;opacity:.85;width:100%;position:absolute;content:"";height:100%;left:0;top:0}@media screen{[data-color-scheme=dark] .documentation-hero[data-v-283b44ff]:after{opacity:.55}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .documentation-hero[data-v-283b44ff]:after{opacity:.55}}.documentation-hero .icon[data-v-283b44ff]{position:absolute;margin-top:10px;margin-right:25px;right:0;width:250px;height:calc(100% - 20px);box-sizing:border-box}@media only screen and (max-width:735px){.documentation-hero .icon[data-v-283b44ff]{display:none}}.documentation-hero .background-icon[data-v-283b44ff]{color:var(--color-documentation-intro-accent,var(--color-figure-gray-secondary));display:block;width:250px;height:auto;opacity:.15;mix-blend-mode:normal;position:absolute;top:50%;left:0;transform:translateY(-50%);max-height:100%}.documentation-hero .background-icon[data-v-283b44ff] img,.documentation-hero .background-icon[data-v-283b44ff] svg{width:100%;height:100%}@media screen{[data-color-scheme=dark] .documentation-hero .background-icon[data-v-283b44ff]{mix-blend-mode:normal;opacity:.15}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .documentation-hero .background-icon[data-v-283b44ff]{mix-blend-mode:normal;opacity:.15}}.documentation-hero__content[data-v-283b44ff]:not(.minimized-hero){padding-top:2.3529411765rem;padding-bottom:2.3529411765rem;position:relative;z-index:1}.full-width-container .documentation-hero__content[data-v-283b44ff]:not(.minimized-hero){max-width:820px;margin-left:auto;margin-right:auto;padding-left:80px;padding-right:80px;box-sizing:border-box}@media print{.full-width-container .documentation-hero__content[data-v-283b44ff]:not(.minimized-hero){padding-left:20px;padding-right:20px;max-width:none}}@media only screen and (min-width:1251px){.full-width-container .documentation-hero__content[data-v-283b44ff]:not(.minimized-hero){max-width:980px}}@media only screen and (min-width:1500px){.full-width-container .documentation-hero__content[data-v-283b44ff]:not(.minimized-hero){max-width:1080px}}@media only screen and (max-width:735px){.full-width-container .documentation-hero__content[data-v-283b44ff]:not(.minimized-hero){width:auto;padding-left:20px;padding-right:20px}}.static-width-container .documentation-hero__content[data-v-283b44ff]:not(.minimized-hero){margin-left:auto;margin-right:auto;width:1536px;width:980px}@media only screen and (max-width:1250px){.static-width-container .documentation-hero__content[data-v-283b44ff]:not(.minimized-hero){width:692px}}@media only screen and (max-width:735px){.static-width-container .documentation-hero__content[data-v-283b44ff]:not(.minimized-hero){width:87.5%}}@media only screen and (max-width:320px){.static-width-container .documentation-hero__content[data-v-283b44ff]:not(.minimized-hero){width:215px}}.documentation-hero .minimized-hero[data-v-283b44ff]{padding:1.3em 1.4em;position:relative;z-index:1}.documentation-hero__above-content[data-v-283b44ff]{position:relative;z-index:1}.documentation-hero--disabled[data-v-283b44ff]{background:none;color:var(--colors-text,var(--color-text))}.documentation-hero--disabled[data-v-283b44ff]:after,.documentation-hero--disabled[data-v-283b44ff]:before{content:none}.short-hero[data-v-283b44ff]{padding-top:3.5294117647rem;padding-bottom:3.5294117647rem}.extra-bottom-padding[data-v-283b44ff]{padding-bottom:3.8235294118rem}ul[data-v-068842ec]{list-style-type:none;margin:0}ul li:first-child .base-link[data-v-068842ec]{margin-top:0}.parent-item .base-link[data-v-068842ec]{font-weight:700}.base-link[data-v-068842ec]{color:var(--color-figure-gray-secondary);font-size:.8235294118rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);display:inline-block;margin:5px 0;transition:color .15s ease-in;max-width:100%}.active .base-link[data-v-068842ec]{color:var(--color-text)}[data-v-3a32ffd0] .code-listing{background:var(--background,var(--color-code-background));color:var(--text,var(--color-code-plain));border-color:var(--colors-grid,var(--color-grid));border-width:var(--code-border-width,1px);border-style:var(--code-border-style,solid)}[data-v-3a32ffd0] .code-listing pre{padding:var(--code-block-style-elements-padding)}[data-v-3a32ffd0] .code-listing pre>code{font-size:.8823529412rem;line-height:1.6666666667;font-weight:400;font-family:var(--typography-html-font-mono,Menlo,monospace)}[data-v-3a32ffd0] *+.code-listing,[data-v-3a32ffd0] *+.endpoint-example,[data-v-3a32ffd0] *+.inline-image-container,[data-v-3a32ffd0] *+aside,[data-v-3a32ffd0] *+figure,[data-v-3a32ffd0] .code-listing+*,[data-v-3a32ffd0] .endpoint-example+*,[data-v-3a32ffd0] .inline-image-container+*,[data-v-3a32ffd0] aside+*,[data-v-3a32ffd0] figure+*{margin-top:var(--spacing-stacked-margin-xlarge)}[data-v-3a32ffd0] *+dl,[data-v-3a32ffd0] dl+*{margin-top:var(--spacing-stacked-margin-large)}[data-v-3a32ffd0] img{display:block;margin:auto;max-width:100%}[data-v-3a32ffd0] ol,[data-v-3a32ffd0] ol li:not(:first-child),[data-v-3a32ffd0] ul,[data-v-3a32ffd0] ul li:not(:first-child){margin-top:var(--spacing-stacked-margin-large)}@media only screen and (max-width:735px){[data-v-3a32ffd0] ol,[data-v-3a32ffd0] ul{margin-left:1.25rem}}[data-v-3a32ffd0] dt:not(:first-child){margin-top:var(--spacing-stacked-margin-large)}[data-v-3a32ffd0] dd{margin-left:2em}.conditional-constraints[data-v-4c6f3ed1] code{color:var(--colors-secondary-label,var(--color-secondary-label))}.highlighted[data-v-798bdf9c]{background:var(--color-syntax-highlighted,mark)}.token-method[data-v-b15299ea]{font-weight:700}.token-keyword[data-v-b15299ea]{color:var(--syntax-keyword,var(--color-syntax-keywords))}.token-number[data-v-b15299ea]{color:var(--syntax-number,var(--color-syntax-numbers))}.token-string[data-v-b15299ea]{color:var(--syntax-string,var(--color-syntax-strings))}.attribute-link[data-v-b15299ea],.token-attribute[data-v-b15299ea]{color:var(--syntax-attribute,var(--color-syntax-keywords))}.token-internalParam[data-v-b15299ea]{color:var(--color-syntax-param-internal-name)}.type-identifier-link[data-v-b15299ea]{color:var(--syntax-type,var(--color-syntax-other-type-names))}.token-removed[data-v-b15299ea]{background-color:var(--color-highlight-red)}.token-added[data-v-b15299ea]{background-color:var(--color-highlight-green)}.source[data-v-d22a3f50]{background:var(--background,var(--color-code-background));border-color:var(--color-grid);color:var(--text,var(--color-code-plain));border-style:solid;border-width:1px;padding:var(--code-block-style-elements-padding);speak:literal-punctuation;line-height:25px;filter:blur(0)}.source.displays-multiple-lines[data-v-d22a3f50],.source[data-v-d22a3f50]{border-radius:var(--border-radius,4px)}.source>code[data-v-d22a3f50]{font-size:.8823529412rem;line-height:1.6666666667;font-weight:400;font-family:var(--typography-html-font-mono,Menlo,monospace);display:block}.expand-enter-active,.expand-leave-active{transition:height .3s ease-in-out;overflow:hidden}.expand-enter,.expand-leave-to{height:0}.platforms[data-v-475cd275]{font-size:.8235294118rem;line-height:1.4285714286;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);margin-bottom:.45rem;margin-top:var(--spacing-stacked-margin-xlarge)}.changed .platforms[data-v-475cd275]{padding-left:.588rem}.platforms[data-v-475cd275]:first-of-type{margin-top:1rem}.declaration-pill[data-v-475cd275]:not(.declaration-pill--expanded) .highlighted{background:unset;font-weight:400}.declaration-pill--expanded[data-v-475cd275]{transition-timing-function:linear;transition-property:opacity,height}.declaration-pill--expanded .source[data-v-475cd275]{border-width:1px}.declaration-pill--expanded .source[data-v-475cd275] a{pointer-events:none}.declaration-pill--expanded>button[data-v-475cd275]{display:block;width:100%}.declaration-pill--expanded .selected-declaration[data-v-475cd275]{border-color:var(--color-focus-border-color,var(--color-focus-border-color))}.declaration-pill--expanded .source[data-v-475cd275]:not(.selected-declaration){background:unset}.declaration-pill--expanded+.declaration-pill--expanded .source[data-v-475cd275]{margin:var(--declaration-code-listing-margin)}.declaration-pill--expanded.expand-enter[data-v-475cd275],.declaration-pill--expanded.expand-leave-to[data-v-475cd275]{opacity:0}.declaration-pill--expanded.expand-enter .source[data-v-475cd275],.declaration-pill--expanded.expand-leave-to .source[data-v-475cd275]{margin:0}.source[data-v-475cd275]{transition:margin .3s linear}.platforms+.source[data-v-475cd275]{margin:0}.changed.declaration-group[data-v-475cd275]{background:var(--background,var(--color-code-background))}.changed .source[data-v-475cd275]{background:none;border:none;margin-top:0;margin-bottom:0;margin-left:2.1764705882rem;padding-left:0}.declaration-diff[data-v-b3e98f40]{background:var(--background,var(--color-code-background))}.declaration-diff-version[data-v-b3e98f40]{padding-left:.588rem;padding-left:2.1764705882rem;font-size:1rem;line-height:1.5294117647;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-figure-gray-secondary);margin:0}.declaration-diff-current[data-v-b3e98f40]{padding-top:8px;padding-bottom:5px}.declaration-diff-previous[data-v-b3e98f40]{padding-top:5px;padding-bottom:8px;background-color:var(--color-changes-modified-previous-background);border-radius:0 0 var(--border-radius,4px) var(--border-radius,4px);position:relative}.declaration-source-link[data-v-5863919c]{font-size:.8235294118rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);display:flex;align-items:center;margin-top:var(--declaration-source-link-margin,var(--spacing-stacked-margin-large))}.declaration-icon[data-v-5863919c]{width:1em;margin-right:5px}.conditional-constraints[data-v-01201553]{margin-top:var(--declaration-conditional-constraints-margin,20px)}.abstract[data-v-f3f57cbe]{font-size:1.2352941176rem;line-height:1.380952381;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:735px){.abstract[data-v-f3f57cbe]{font-size:1.1176470588rem;line-height:1.4210526316;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}[data-v-f3f57cbe] p:last-child{margin-bottom:0}.container[data-v-6e075935]{padding-bottom:40px}.full-width-container .container[data-v-6e075935]{max-width:820px;margin-left:auto;margin-right:auto;padding-left:80px;padding-right:80px;box-sizing:border-box}@media print{.full-width-container .container[data-v-6e075935]{padding-left:20px;padding-right:20px;max-width:none}}@media only screen and (min-width:1251px){.full-width-container .container[data-v-6e075935]{max-width:980px}}@media only screen and (min-width:1500px){.full-width-container .container[data-v-6e075935]{max-width:1080px}}@media only screen and (max-width:735px){.full-width-container .container[data-v-6e075935]{width:auto;padding-left:20px;padding-right:20px}}.static-width-container .container[data-v-6e075935]{margin-left:auto;margin-right:auto;width:1536px;width:980px}@media only screen and (max-width:1250px){.static-width-container .container[data-v-6e075935]{width:692px}}@media only screen and (max-width:735px){.static-width-container .container[data-v-6e075935]{width:87.5%}}@media only screen and (max-width:320px){.static-width-container .container[data-v-6e075935]{width:215px}}.title[data-v-6e075935]{font-size:1.8823529412rem;line-height:1.125;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);padding-top:40px;border-top-color:var(--color-grid);border-top-style:solid;border-top-width:var(--content-table-title-border-width,1px)}@media only screen and (max-width:1250px){.title[data-v-6e075935]{font-size:1.6470588235rem;line-height:1.1428571429;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){.title[data-v-6e075935]{font-size:1.4117647059rem;line-height:1.1666666667;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.title+.contenttable-section[data-v-1b0546d9]{margin-top:0}.contenttable-section[data-v-1b0546d9]{align-items:baseline;padding-top:2.353rem}.contenttable-section[data-v-1b0546d9]:last-child{margin-bottom:0}[data-v-1b0546d9] .contenttable-title{font-size:1.4117647059rem;line-height:1.1666666667;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:1250px){[data-v-1b0546d9] .contenttable-title{font-size:1.2352941176rem;line-height:1.1904761905;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){.contenttable-section[data-v-1b0546d9]{align-items:unset;border-top:none;display:inherit;margin:0}.section-content[data-v-1b0546d9],.section-title[data-v-1b0546d9]{padding:0}[data-v-1b0546d9] .contenttable-title{margin:0 0 2.353rem 0;padding-bottom:.5rem}}.badge[data-v-04624022]{--badge-color:var(--color-badge-default);--badge-dark-color:var(--color-badge-dark-default);font-size:.7058823529rem;line-height:1.3333333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);display:inline-block;padding:2px 4px;white-space:nowrap;border-radius:var(--badge-border-radius,1px);border-style:var(--badge-border-style,none);border-width:var(--badge-border-width,1px);margin:auto;margin-left:5px;color:var(--colors-badge-text,var(--color-badge-text));background-color:var(--badge-color)}@media screen{[data-color-scheme=dark] .badge[data-v-04624022]{background-color:var(--badge-dark-color)}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .badge[data-v-04624022]{background-color:var(--badge-dark-color)}}.badge-deprecated[data-v-04624022]{--badge-color:var(--color-badge-deprecated);--badge-dark-color:var(--color-badge-dark-deprecated)}.badge-beta[data-v-04624022]{--badge-color:var(--color-badge-beta);--badge-dark-color:var(--color-badge-dark-beta)}.topic-icon-wrapper[data-v-55f9d05d]{display:flex;align-items:center;justify-content:center;height:1.4705882353rem;flex:0 0 1.294rem;width:1.294rem;margin-right:1rem}.topic-icon[data-v-55f9d05d]{height:.8823529412rem;transform:scale(1);-webkit-transform:scale(1);overflow:visible}.topic-icon[data-v-55f9d05d] img{margin:0;display:block;width:100%;height:100%;-o-object-fit:contain;object-fit:contain}.topic-icon.curly-brackets-icon[data-v-55f9d05d]{height:1rem}.decorator[data-v-17c84f82],.label[data-v-17c84f82]{color:var(--colors-secondary-label,var(--color-secondary-label))}.label[data-v-17c84f82]{font-size:1rem;line-height:1.4705882353;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.empty-token[data-v-17c84f82]{font-size:0}.empty-token[data-v-17c84f82]:after{content:" ";font-size:1rem}.abstract[data-v-0d9c6bcc],.link-block[data-v-0d9c6bcc] .badge{margin-left:2.294rem}.link-block .badge+.badge[data-v-0d9c6bcc]{margin-left:1rem}.link[data-v-0d9c6bcc]{display:flex}.link-block .badge[data-v-0d9c6bcc]{margin-top:.5rem}.link-block.has-inline-element[data-v-0d9c6bcc]{display:flex;align-items:flex-start;flex-flow:row wrap}.link-block.has-inline-element .badge[data-v-0d9c6bcc]{margin-left:1rem;margin-top:0}.link-block .has-adjacent-elements[data-v-0d9c6bcc]{padding-top:5px;padding-bottom:5px;display:inline-flex}.link-block[data-v-0d9c6bcc],.link[data-v-0d9c6bcc]{box-sizing:inherit}.link-block.changed[data-v-0d9c6bcc],.link.changed[data-v-0d9c6bcc]{padding-right:1rem;padding-left:2.1764705882rem;padding-top:8px;padding-bottom:8px;display:inline-flex;width:100%;box-sizing:border-box}.link-block.changed.changed[data-v-0d9c6bcc],.link.changed.changed[data-v-0d9c6bcc]{padding-right:1rem}@media only screen and (max-width:735px){.link-block.changed[data-v-0d9c6bcc],.link.changed[data-v-0d9c6bcc]{padding-left:0;padding-right:0}.link-block.changed.changed[data-v-0d9c6bcc],.link.changed.changed[data-v-0d9c6bcc]{padding-right:17px;padding-left:2.1764705882rem}.link-block.changed[data-v-0d9c6bcc],.link.changed[data-v-0d9c6bcc]{padding-left:0;padding-right:0}}.abstract .topic-required[data-v-0d9c6bcc]:not(:first-child){margin-top:4px}.topic-required[data-v-0d9c6bcc]{font-size:.8em}.deprecated[data-v-0d9c6bcc]{text-decoration:line-through}.section-content>.content[data-v-1c2724f5],.topic[data-v-1c2724f5]{margin-top:15px}.no-title .section-content>.content[data-v-1c2724f5]:first-child,.no-title .topic[data-v-1c2724f5]:first-child{margin-top:0}.property-metadata[data-v-f911f232]{color:var(--color-figure-gray-secondary)}.parameter-attributes[data-v-c0edcb84]{margin-left:1rem}[data-v-c0edcb84] .property-metadata{color:currentColor}.datalist dd{padding-left:2rem}.datalist dt{font-weight:600;padding-left:1rem;padding-top:var(--spacing-param)}.datalist dt:first-of-type{padding-top:0}.type[data-v-791bac44]:first-letter{text-transform:capitalize}.detail-type[data-v-d66cd00c]{font-weight:600;padding-left:1rem;padding-top:var(--spacing-param)}.detail-type[data-v-d66cd00c]:first-child{padding-top:0}@media only screen and (max-width:735px){.detail-type[data-v-d66cd00c]{padding-left:0}}.detail-content[data-v-d66cd00c]{padding-left:2rem}@media only screen and (max-width:735px){.detail-content[data-v-d66cd00c]{padding-left:0}}.param-name[data-v-5ef1227e]{font-weight:600;padding-left:1rem;padding-top:var(--spacing-param)}.param-name[data-v-5ef1227e]:first-child{padding-top:0}@media only screen and (max-width:735px){.param-name[data-v-5ef1227e]{padding-left:0}}.param-content[data-v-5ef1227e]{padding-left:2rem}@media only screen and (max-width:735px){.param-content[data-v-5ef1227e]{padding-left:0}}.param-content[data-v-5ef1227e] dt{font-weight:600}.param-content[data-v-5ef1227e] dd{margin-left:1em}.parameters-table[data-v-eee7e94e] .change-added,.parameters-table[data-v-eee7e94e] .change-removed{display:inline-block;max-width:100%}.parameters-table[data-v-eee7e94e] .change-removed,.parameters-table[data-v-eee7e94e] .token-removed{text-decoration:line-through}.param[data-v-eee7e94e]{font-size:.8823529412rem;box-sizing:border-box}.param.changed[data-v-eee7e94e]{display:flex;flex-flow:row wrap;padding-right:1rem;padding-left:2.1764705882rem;padding-top:8px;padding-bottom:8px;display:inline-flex;width:100%;box-sizing:border-box}.param.changed.changed[data-v-eee7e94e]{padding-right:1rem}@media only screen and (max-width:735px){.param.changed[data-v-eee7e94e]{padding-left:0;padding-right:0}.param.changed.changed[data-v-eee7e94e]{padding-right:17px;padding-left:2.1764705882rem}.param.changed[data-v-eee7e94e]{padding-left:0;padding-right:0}}.param.changed.changed[data-v-eee7e94e]{padding-left:0;padding-right:0}.param.changed+.param.changed[data-v-eee7e94e]{margin-top:calc(var(--spacing-param)/2)}.changed .param-content[data-v-eee7e94e],.changed .param-symbol[data-v-eee7e94e]{padding-top:2px;padding-bottom:2px}@media only screen and (max-width:735px){.changed .param-content[data-v-eee7e94e]{padding-top:0}.changed .param-symbol[data-v-eee7e94e]{padding-bottom:0}}.param-symbol[data-v-eee7e94e]{text-align:right}.changed .param-symbol[data-v-eee7e94e]{padding-left:2.1764705882rem}@media only screen and (max-width:735px){.param-symbol[data-v-eee7e94e]{text-align:left}.changed .param-symbol[data-v-eee7e94e]{padding-left:0}}.param-symbol[data-v-eee7e94e] .type-identifier-link{color:var(--color-link)}.param+.param[data-v-eee7e94e]{margin-top:var(--spacing-param)}.param+.param[data-v-eee7e94e]:first-child{margin-top:0}.param-content[data-v-eee7e94e]{padding-left:1rem;padding-left:2.1764705882rem}.changed .param-content[data-v-eee7e94e]{padding-right:1rem}@media only screen and (max-width:735px){.param-content[data-v-eee7e94e]{padding-left:0;padding-right:0}}.property-text{font-weight:700}.property-metadata[data-v-549ed0a8]{color:var(--color-figure-gray-secondary)}.property-name[data-v-39899ccf]{font-weight:700}.property-name.deprecated[data-v-39899ccf]{text-decoration:line-through}.property-deprecated[data-v-39899ccf]{margin-left:0}.content[data-v-39899ccf],.content[data-v-39899ccf] p:first-child{display:inline}.response-mimetype[data-v-18890a0f]{color:var(--color-figure-gray-secondary)}.part-name[data-v-68facc94]{font-weight:700}.content[data-v-68facc94],.content[data-v-68facc94] p:first-child{display:inline}.param-name[data-v-0d9b752e]{font-weight:700}.param-name.deprecated[data-v-0d9b752e]{text-decoration:line-through}.param-deprecated[data-v-0d9b752e]{margin-left:0}.content[data-v-0d9b752e],.content[data-v-0d9b752e] p:first-child{display:inline}.response-name[data-v-362f5b54],.response-reason[data-v-362f5b54]{font-weight:700}@media only screen and (max-width:735px){.response-reason[data-v-362f5b54]{display:none}}.response-name>code>.reason[data-v-362f5b54]{display:none}@media only screen and (max-width:735px){.response-name>code>.reason[data-v-362f5b54]{display:initial}}.link[data-v-241f4141]{display:flex;margin-bottom:.5rem}.link-block[data-v-241f4141],.link[data-v-241f4141]{box-sizing:inherit}.link-block.changed[data-v-241f4141],.link.changed[data-v-241f4141]{padding-right:1rem;padding-left:2.1764705882rem;padding-top:8px;padding-bottom:8px;display:inline-flex;width:100%;box-sizing:border-box}.link-block.changed.changed[data-v-241f4141],.link.changed.changed[data-v-241f4141]{padding-right:1rem}@media only screen and (max-width:735px){.link-block.changed[data-v-241f4141],.link.changed[data-v-241f4141]{padding-left:0;padding-right:0}.link-block.changed.changed[data-v-241f4141],.link.changed.changed[data-v-241f4141]{padding-right:17px;padding-left:2.1764705882rem}.link-block.changed[data-v-241f4141],.link.changed[data-v-241f4141]{padding-left:0;padding-right:0}}.mention-icon[data-v-241f4141]{margin-right:.25rem}.primary-content.with-border[data-v-65c116be]:before{border-top-color:var(--colors-grid,var(--color-grid));border-top-style:solid;border-top-width:var(--content-table-title-border-width,1px);content:"";display:block}.primary-content[data-v-65c116be]>*{margin-bottom:40px;margin-top:40px}.primary-content[data-v-65c116be]>:first-child{margin-top:2.353rem}.relationships-list[data-v-ba5cad92]{list-style:none}.relationships-list.column[data-v-ba5cad92]{margin-left:0;margin-top:15px}.relationships-list.inline[data-v-ba5cad92]{display:flex;flex-direction:row;flex-wrap:wrap;margin-top:15px;margin-left:0}.relationships-list.inline li[data-v-ba5cad92]:not(:last-child):after{content:", "}.relationships-list.changed[data-v-ba5cad92]{padding-right:1rem;padding-left:2.1764705882rem;padding-top:8px;padding-bottom:8px;display:inline-flex;width:100%;box-sizing:border-box}.relationships-list.changed.changed[data-v-ba5cad92]{padding-right:1rem}@media only screen and (max-width:735px){.relationships-list.changed[data-v-ba5cad92]{padding-left:0;padding-right:0}.relationships-list.changed.changed[data-v-ba5cad92]{padding-right:17px;padding-left:2.1764705882rem}.relationships-list.changed[data-v-ba5cad92]{padding-left:0;padding-right:0}}.relationships-list.changed[data-v-ba5cad92]:after{margin-top:.6176470588rem}.relationships-list.changed.column[data-v-ba5cad92]{display:block;box-sizing:border-box}.relationships-item[data-v-ba5cad92],.relationships-list[data-v-ba5cad92]{box-sizing:inherit}.conditional-constraints[data-v-ba5cad92]{font-size:.8235294118rem;margin:.1764705882rem 0 .5882352941rem 1.1764705882rem}.platform[data-v-3da26baa],.technology[data-v-3da26baa]{display:inline-flex;align-items:center}.tech-icon[data-v-3da26baa]{height:12px;padding-right:5px;--color-svg-icon:var(--color-figure-gray)}.changed[data-v-3da26baa]{padding-left:17px;border:none}.changed[data-v-3da26baa]:after{all:unset}.changed[data-v-3da26baa]:before{background-image:url(../img/modified-icon.efb2697d.svg);background-repeat:no-repeat;bottom:0;content:" ";margin:auto;margin-right:8px;position:absolute;top:0;width:20px;height:20px;margin:0;left:-5px}@media screen{[data-color-scheme=dark] .changed[data-v-3da26baa]:before{background-image:url(../img/modified-icon.efb2697d.svg)}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .changed[data-v-3da26baa]:before{background-image:url(../img/modified-icon.efb2697d.svg)}}.changed-added[data-v-3da26baa]:before{background-image:url(../img/added-icon.832a5d2c.svg)}@media screen{[data-color-scheme=dark] .changed-added[data-v-3da26baa]:before{background-image:url(../img/added-icon.832a5d2c.svg)}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .changed-added[data-v-3da26baa]:before{background-image:url(../img/added-icon.832a5d2c.svg)}}.changed-deprecated[data-v-3da26baa]:before{background-image:url(../img/deprecated-icon.7bf1740a.svg)}@media screen{[data-color-scheme=dark] .changed-deprecated[data-v-3da26baa]:before{background-image:url(../img/deprecated-icon.7bf1740a.svg)}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .changed-deprecated[data-v-3da26baa]:before{background-image:url(../img/deprecated-icon.7bf1740a.svg)}}.availability[data-v-3da26baa]{display:flex;flex-flow:row wrap;gap:10px;margin-top:.8823529412rem;font-size:.8235294118rem;line-height:1.4285714286;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.availability>[data-v-3da26baa]:after{content:"";display:inline-block;width:1px;height:1em;background:currentColor;margin-left:10px}.availability>[data-v-3da26baa]:last-child:after{content:none}.topictitle[data-v-6630a012]{margin-bottom:.7058823529rem}.topictitle[data-v-6630a012]:last-child{margin-bottom:0}@media only screen and (max-width:735px){.topictitle[data-v-6630a012]{margin:0}}.eyebrow[data-v-6630a012]{font-size:1.2352941176rem;line-height:1.1904761905;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-documentation-intro-eyebrow,var(--colors-secondary-label,var(--color-secondary-label)));display:block;margin-bottom:.8823529412rem}@media only screen and (max-width:735px){.eyebrow[data-v-6630a012]{font-size:1.1176470588rem;line-height:1.2105263158;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.title[data-v-6630a012]{font-size:2.3529411765rem;line-height:1.1;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-documentation-intro-title,var(--colors-header-text,var(--color-header-text)))}@media only screen and (max-width:1250px){.title[data-v-6630a012]{font-size:1.8823529412rem;line-height:1.125;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){.title[data-v-6630a012]{font-size:1.6470588235rem;line-height:1.1428571429;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}small[data-v-6630a012]{font-size:1.4117647059rem;line-height:1.1666666667;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);padding-left:10px}@media only screen and (max-width:1250px){small[data-v-6630a012]{font-size:1.2352941176rem;line-height:1.1904761905;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}small[data-v-6630a012]:before{content:attr(data-tag-name)}small.Beta[data-v-6630a012]{color:var(--color-badge-beta)}@media screen{[data-color-scheme=dark] small.Beta[data-v-6630a012]{color:var(--color-badge-dark-beta)}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] small.Beta[data-v-6630a012]{color:var(--color-badge-dark-beta)}}small.Deprecated[data-v-6630a012]{color:var(--color-badge-deprecated)}@media screen{[data-color-scheme=dark] small.Deprecated[data-v-6630a012]{color:var(--color-badge-dark-deprecated)}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] small.Deprecated[data-v-6630a012]{color:var(--color-badge-dark-deprecated)}}.OnThisPageStickyContainer[data-v-39ac6ed0]{margin-top:2.353rem;position:sticky;top:3.8235294118rem;align-self:flex-start;flex:0 0 auto;width:192px;padding-right:1.2941176471rem;box-sizing:border-box;padding-bottom:var(--spacing-stacked-margin-small);max-height:calc(100vh - 3.82353rem);overflow:auto}@media print{.OnThisPageStickyContainer[data-v-39ac6ed0]{display:none}}@media only screen and (max-width:735px){.OnThisPageStickyContainer[data-v-39ac6ed0]{display:none}}.nav-menu-link[data-v-2ad31daf]{display:inline-block;line-height:22px;white-space:nowrap}.nav--in-breakpoint-range .nav-menu-link[data-v-2ad31daf]{line-height:42px;border-top:1px solid;border-color:var(--color-nav-rule);display:flex;flex:1 0 100%;height:100%;align-items:center}.theme-dark.nav--in-breakpoint-range .nav-menu-link[data-v-2ad31daf]{border-color:var(--color-nav-dark-rule)}.hierarchy-collapsed-items[data-v-7b701104]{position:relative;display:inline-flex;align-items:center}.hierarchy-collapsed-items[data-v-7b701104]:before{content:"/";width:.2941176471rem;margin:0 .2941176471rem}.nav--in-breakpoint-range .hierarchy-collapsed-items[data-v-7b701104],:root.no-js .hierarchy-collapsed-items[data-v-7b701104]{display:none}.hierarchy-collapsed-items .toggle[data-v-7b701104]{background:var(--color-nav-hierarchy-collapse-background);border-color:var(--color-nav-hierarchy-collapse-borders);border-radius:var(--border-radius,4px);border-style:solid;border-width:0;font-weight:600;height:1.1176470588rem;text-align:center;width:2.1176470588rem;display:flex;align-items:center;justify-content:center}.theme-dark .hierarchy-collapsed-items .toggle[data-v-7b701104]{background:var(--color-nav-dark-hierarchy-collapse-background)}.hierarchy-collapsed-items .toggle.focused[data-v-7b701104],.hierarchy-collapsed-items .toggle[data-v-7b701104]:active,.hierarchy-collapsed-items .toggle[data-v-7b701104]:focus{box-shadow:0 0 0 4px var(--color-focus-color);outline:none}.indicator[data-v-7b701104]{width:1em;height:1em;display:flex;align-items:center}.indicator .toggle-icon[data-v-7b701104]{width:100%}.dropdown[data-v-7b701104]{background:var(--color-nav-hierarchy-collapse-background);margin:0;list-style-type:none;border-color:var(--color-nav-hierarchy-collapse-borders);border-radius:var(--border-radius,4px);border-style:solid;box-shadow:0 1px 4px -1px var(--color-figure-gray-secondary);border-width:0;padding:0 .5rem;position:absolute;z-index:42;top:calc(100% + .41176rem)}.theme-dark .dropdown[data-v-7b701104]{background:var(--color-nav-dark-hierarchy-collapse-background);border-color:var(--color-nav-dark-hierarchy-collapse-borders)}.dropdown.collapsed[data-v-7b701104]{opacity:0;transform:translate3d(0,-.4117647059rem,0);transition:opacity .25s ease,transform .25s ease,visibility 0s linear .25s;visibility:hidden}.dropdown[data-v-7b701104]:not(.collapsed){opacity:1;transform:none;transition:opacity .25s ease,transform .25s ease,visibility 0s linear 0s;visibility:visible}.nav--in-breakpoint-range .dropdown[data-v-7b701104]:not(.collapsed){display:none}.dropdown[data-v-7b701104]:before{border-bottom-color:var(--color-nav-hierarchy-collapse-background);border-bottom-style:solid;border-bottom-width:.5rem;border-left-color:transparent;border-left-style:solid;border-left-width:.5rem;border-right-color:transparent;border-right-style:solid;border-right-width:.5rem;content:"";left:1.4411764706rem;position:absolute;top:-.4411764706rem}.theme-dark .dropdown[data-v-7b701104]:before{border-bottom-color:var(--color-nav-dark-hierarchy-collapse-background)}.dropdown-item[data-v-7b701104]{border-top-color:var(--color-nav-hierarchy-collapse-borders);border-top-style:solid;border-top-width:1px}.theme-dark .dropdown-item[data-v-7b701104]{border-top-color:var(--color-nav-dark-hierarchy-collapse-borders)}.dropdown-item[data-v-7b701104]:first-child{border-top:none}.nav-menu-link[data-v-7b701104]{max-width:57.6470588235rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;padding:.75rem 1rem}.hierarchy-item[data-v-13293168]{display:flex;align-items:center;margin-left:0}.hierarchy-item[data-v-13293168]:not(:first-child):before{content:"/";width:.2941176471rem;margin:0 .2941176471rem}.nav--in-breakpoint-range .hierarchy-item[data-v-13293168]{border-top:1px solid var(--color-nav-hierarchy-item-borders);display:flex;align-items:center}.theme-dark.nav--in-breakpoint-range .hierarchy-item[data-v-13293168]{border-top-color:var(--color-nav-dark-hierarchy-item-borders)}.nav--in-breakpoint-range .hierarchy-item[data-v-13293168]:first-of-type{border-top:none}.hierarchy-item.collapsed[data-v-13293168]{display:none}:root.no-js .hierarchy-item.collapsed[data-v-13293168]{display:flex}.nav--in-breakpoint-range .hierarchy-item.collapsed[data-v-13293168]{display:inline-block}.item[data-v-13293168]{display:inline-block;vertical-align:middle}.nav--in-breakpoint-range .item[data-v-13293168]{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;height:100%;line-height:2.4705882353rem}@media only screen and (min-width:768px){.hierarchy-item:first-child:last-child .item[data-v-13293168],.hierarchy-item:first-child:last-child~.hierarchy-item .item[data-v-13293168]{max-width:45rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.has-badge .hierarchy-item:first-child:last-child .item[data-v-13293168],.has-badge .hierarchy-item:first-child:last-child~.hierarchy-item .item[data-v-13293168],.hierarchy-item:first-child:nth-last-child(2) .item[data-v-13293168],.hierarchy-item:first-child:nth-last-child(2)~.hierarchy-item .item[data-v-13293168]{max-width:36rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.has-badge .hierarchy-item:first-child:nth-last-child(2) .item[data-v-13293168],.has-badge .hierarchy-item:first-child:nth-last-child(2)~.hierarchy-item .item[data-v-13293168]{max-width:28.8rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hierarchy-item:first-child:nth-last-child(3) .item[data-v-13293168],.hierarchy-item:first-child:nth-last-child(3)~.hierarchy-item .item[data-v-13293168]{max-width:27rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.has-badge .hierarchy-item:first-child:nth-last-child(3) .item[data-v-13293168],.has-badge .hierarchy-item:first-child:nth-last-child(3)~.hierarchy-item .item[data-v-13293168]{max-width:21.6rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hierarchy-item:first-child:nth-last-child(4) .item[data-v-13293168],.hierarchy-item:first-child:nth-last-child(4)~.hierarchy-item .item[data-v-13293168]{max-width:18rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.has-badge .hierarchy-item:first-child:nth-last-child(4) .item[data-v-13293168],.has-badge .hierarchy-item:first-child:nth-last-child(4)~.hierarchy-item .item[data-v-13293168]{max-width:14.4rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hierarchy-item:first-child:nth-last-child(5) .item[data-v-13293168],.hierarchy-item:first-child:nth-last-child(5)~.hierarchy-item .item[data-v-13293168]{max-width:9rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.has-badge .hierarchy-item:first-child:nth-last-child(5) .item[data-v-13293168],.has-badge .hierarchy-item:first-child:nth-last-child(5)~.hierarchy-item .item[data-v-13293168]{max-width:7.2rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hierarchy-collapsed-items~.hierarchy-item .item[data-v-13293168]{max-width:10.8rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hierarchy-collapsed-items~.hierarchy-item:last-child .item[data-v-13293168]{max-width:none}.has-badge .hierarchy-collapsed-items~.hierarchy-item .item[data-v-13293168]{max-width:8.64rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}.hierarchy[data-v-d54f3438]{font-size:.8235294118rem;line-height:1.4285714286;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);justify-content:flex-start;align-items:center;margin:0 0 1.1764705882rem 0;min-width:0}.nav--in-breakpoint-range .hierarchy[data-v-d54f3438]{margin:0}.hierarchy .root-hierarchy .item[data-v-d54f3438]{max-width:10rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}[data-v-d54f3438] a.nav-menu-link{color:inherit;text-decoration:underline}.declaration-list-menu[data-v-59c7b9d0]{position:relative;width:100%}.declaration-list-menu .declaration-list-toggle[data-v-59c7b9d0]{display:flex;flex-direction:row;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background-color:var(--color-other-decl-button);padding:5px 15px;color:var(--colors-link,var(--color-link));z-index:1;gap:5px;white-space:nowrap;align-items:center}.declaration-list-menu .icon[data-v-59c7b9d0]{display:flex}.declaration-list-menu .icon svg[data-v-59c7b9d0]{transition-duration:.4s;transition-timing-function:linear;transition-property:transform;width:15px;height:15px;fill:var(--colors-link,var(--color-link))}.declaration-list-menu .icon svg.expand[data-v-59c7b9d0]{transform:rotate(45deg)}.doc-topic[data-v-59c7b9d0]{display:flex;flex-direction:column;height:100%}.doc-topic.with-on-this-page[data-v-59c7b9d0]{--doc-hero-right-offset:192px}#main[data-v-59c7b9d0]{outline-style:none;height:100%}[data-v-59c7b9d0] .minimized-title{margin-bottom:.833rem}[data-v-59c7b9d0] .minimized-title .title{font-size:1.416rem;font-weight:700}[data-v-59c7b9d0] .minimized-title small{font-size:1rem;padding-left:.416rem}.minimized-abstract[data-v-59c7b9d0]{font-size:1rem;line-height:1.4705882353;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.container[data-v-59c7b9d0]:not(.minimized-container){outline-style:none}.full-width-container .container[data-v-59c7b9d0]:not(.minimized-container){max-width:820px;margin-left:auto;margin-right:auto;padding-left:80px;padding-right:80px;box-sizing:border-box}@media print{.full-width-container .container[data-v-59c7b9d0]:not(.minimized-container){padding-left:20px;padding-right:20px;max-width:none}}@media only screen and (min-width:1251px){.full-width-container .container[data-v-59c7b9d0]:not(.minimized-container){max-width:980px}}@media only screen and (min-width:1500px){.full-width-container .container[data-v-59c7b9d0]:not(.minimized-container){max-width:1080px}}@media only screen and (max-width:735px){.full-width-container .container[data-v-59c7b9d0]:not(.minimized-container){width:auto;padding-left:20px;padding-right:20px}}.static-width-container .container[data-v-59c7b9d0]:not(.minimized-container){margin-left:auto;margin-right:auto;width:1536px;width:980px}@media only screen and (max-width:1250px){.static-width-container .container[data-v-59c7b9d0]:not(.minimized-container){width:692px}}@media only screen and (max-width:735px){.static-width-container .container[data-v-59c7b9d0]:not(.minimized-container){width:87.5%}}@media only screen and (max-width:320px){.static-width-container .container[data-v-59c7b9d0]:not(.minimized-container){width:215px}}[data-v-59c7b9d0] .minimized-container{outline-style:none;--spacing-stacked-margin-large:0.667em;--spacing-stacked-margin-xlarge:1em;--declaration-code-listing-margin:1em 0 0 0;--declaration-conditional-constraints-margin:1em;--declaration-source-link-margin:0.833em;--code-block-style-elements-padding:7px 12px;--spacing-param:var(--spacing-stacked-margin-large);--aside-border-radius:6px;--code-border-radius:6px}[data-v-59c7b9d0] .minimized-container .description{margin-bottom:1.5em}[data-v-59c7b9d0] .minimized-container>.primary-content>*{margin-top:1.5em;margin-bottom:1.5em}[data-v-59c7b9d0] .minimized-container .description{margin-top:0}[data-v-59c7b9d0] .minimized-container h1,[data-v-59c7b9d0] .minimized-container h2,[data-v-59c7b9d0] .minimized-container h3,[data-v-59c7b9d0] .minimized-container h4,[data-v-59c7b9d0] .minimized-container h5,[data-v-59c7b9d0] .minimized-container h6{font-size:1rem;font-weight:700}[data-v-59c7b9d0] .minimized-container h2{font-size:1.083rem}[data-v-59c7b9d0] .minimized-container h1{font-size:1.416rem}[data-v-59c7b9d0] .minimized-container aside{padding:.667rem 1rem}[data-v-59c7b9d0] .minimized-container .source{border-radius:var(--code-border-radius);margin:var(--declaration-code-listing-margin)}[data-v-59c7b9d0] .minimized-container .single-line{border-radius:var(--code-border-radius)}.description[data-v-59c7b9d0]{margin-bottom:2.353rem}.description[data-v-59c7b9d0]:empty{display:none}.description.after-enhanced-hero[data-v-59c7b9d0]{margin-top:2.353rem}.description[data-v-59c7b9d0] .content+*{margin-top:var(--spacing-stacked-margin-large)}.full-width-container .doc-content .minimized-container[data-v-59c7b9d0]{padding-left:1.4rem;padding-right:1.4rem}[data-v-59c7b9d0] .no-primary-content{--content-table-title-border-width:0px}.sample-download[data-v-59c7b9d0]{margin-top:20px}.declarations-container[data-v-59c7b9d0]{margin-top:40px}.declarations-container.minimized-container[data-v-59c7b9d0]{margin-top:0}[data-v-59c7b9d0] h1{font-size:2.3529411765rem;line-height:1.1;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:1250px){[data-v-59c7b9d0] h1{font-size:1.8823529412rem;line-height:1.125;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){[data-v-59c7b9d0] h1{font-size:1.6470588235rem;line-height:1.1428571429;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}[data-v-59c7b9d0] h2{font-size:1.8823529412rem;line-height:1.125;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:1250px){[data-v-59c7b9d0] h2{font-size:1.6470588235rem;line-height:1.1428571429;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){[data-v-59c7b9d0] h2{font-size:1.4117647059rem;line-height:1.1666666667;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}[data-v-59c7b9d0] h3{font-size:1.6470588235rem;line-height:1.1428571429;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:1250px){[data-v-59c7b9d0] h3{font-size:1.4117647059rem;line-height:1.1666666667;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){[data-v-59c7b9d0] h3{font-size:1.2352941176rem;line-height:1.1904761905;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}[data-v-59c7b9d0] h4{font-size:1.4117647059rem;line-height:1.1666666667;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:1250px){[data-v-59c7b9d0] h4{font-size:1.2352941176rem;line-height:1.1904761905;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}[data-v-59c7b9d0] h5{font-size:1.2941176471rem;line-height:1.1818181818;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:1250px){[data-v-59c7b9d0] h5{font-size:1.1764705882rem;line-height:1.2;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){[data-v-59c7b9d0] h5{font-size:1.0588235294rem;line-height:1.4444444444;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}[data-v-59c7b9d0] h6{font-size:1rem;line-height:1.4705882353;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.doc-content-wrapper[data-v-59c7b9d0]{display:flex;justify-content:center}.doc-content-wrapper .doc-content[data-v-59c7b9d0]{min-width:0;width:100%}.doc-content-wrapper .doc-content .container:only-child .declaration-list-menu[data-v-59c7b9d0]:last-child:before{border-top-color:var(--colors-grid,var(--color-grid));border-top-style:solid;border-top-width:var(--content-table-title-border-width,1px);content:"";display:block;margin-bottom:40px}.with-on-this-page .doc-content-wrapper .doc-content[data-v-59c7b9d0]{max-width:820px}@media only screen and (min-width:1251px){.with-on-this-page .doc-content-wrapper .doc-content[data-v-59c7b9d0]{max-width:980px}}@media only screen and (min-width:1500px){.with-on-this-page .doc-content-wrapper .doc-content[data-v-59c7b9d0]{max-width:1080px}}.quick-navigation-open[data-v-96c35eb8]{display:flex;align-items:center;justify-content:center;width:16px;border:1px solid var(--color-grid);height:100%;border-radius:var(--border-radius,4px);transition:background-color .15s;box-sizing:border-box}.quick-navigation-open[data-v-96c35eb8]:hover{background-color:var(--color-fill-tertiary)}@media only screen and (max-width:1023px){.quick-navigation-open[data-v-96c35eb8]{display:none}}.fromkeyboard .quick-navigation-open[data-v-96c35eb8]:focus{box-shadow:0 0 0 4px var(--color-focus-color);outline:none;border-color:var(--color-focus-border-color)}.tag[data-v-7e76f326]{display:inline-block;padding-right:.5882352941rem}.tag[data-v-7e76f326]:focus{outline:none}.tag button[data-v-7e76f326]{color:var(--color-figure-gray);background-color:var(--color-fill-tertiary);font-size:.8235294118rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);border-radius:.8235294118rem;padding:.2352941176rem .5882352941rem;white-space:nowrap;border:1px solid transparent}@media(hover:hover){.tag button[data-v-7e76f326]:hover{transition:background-color .2s,color .2s;background-color:var(--color-fill-blue);color:#fff}}.tag button[data-v-7e76f326]:focus:active{background-color:var(--color-fill-blue);color:#fff}.fromkeyboard .tag button[data-v-7e76f326]:focus,.tag button.focus[data-v-7e76f326],.tag button[data-v-7e76f326]:focus{box-shadow:0 0 0 4px var(--color-focus-color);outline:none;border-color:var(--color-focus-border-color)}.tags[data-v-1f2bd813]{position:relative;margin:0;list-style:none;box-sizing:border-box;transition:padding-right .8s,padding-bottom .8s,max-height 1s,opacity 1s;padding:0}.tags .scroll-wrapper[data-v-1f2bd813]{overflow-x:auto;overflow-y:hidden;-ms-overflow-style:none;scrollbar-color:var(--color-figure-gray-tertiary) transparent;scrollbar-width:thin}.tags .scroll-wrapper[data-v-1f2bd813]::-webkit-scrollbar{height:0}@supports not ((-webkit-touch-callout:none) or (scrollbar-width:none) or (-ms-overflow-style:none)){.tags .scroll-wrapper.scrolling[data-v-1f2bd813]{--scrollbar-height:11px;padding-top:var(--scrollbar-height);height:calc(var(--scroll-target-height) - var(--scrollbar-height));display:flex;align-items:center}}.tags .scroll-wrapper.scrolling[data-v-1f2bd813]::-webkit-scrollbar{height:11px}.tags .scroll-wrapper.scrolling[data-v-1f2bd813]::-webkit-scrollbar-thumb{border-radius:10px;background-color:var(--color-figure-gray-tertiary);border:2px solid transparent;background-clip:padding-box}.tags .scroll-wrapper.scrolling[data-v-1f2bd813]::-webkit-scrollbar-track-piece:end{margin-right:8px}.tags .scroll-wrapper.scrolling[data-v-1f2bd813]::-webkit-scrollbar-track-piece:start{margin-left:8px}.tags ul[data-v-1f2bd813]{margin:0;padding:0;display:flex}.filter[data-v-9ad1ed4c]{--input-vertical-padding:0.7647058824rem;--input-horizontal-spacing:0.5882352941rem;--input-height:1.6470588235rem;--input-border-color:var(--color-fill-gray-secondary);--input-text:var(--color-fill-gray-secondary);position:relative;box-sizing:border-box;-webkit-tap-highlight-color:rgba(0,0,0,0);border-radius:calc(var(--border-radius, 4px) + 1px)}.fromkeyboard .filter[data-v-9ad1ed4c]:focus{outline:none}.filter__top-wrapper[data-v-9ad1ed4c]{display:flex}.filter__filter-button[data-v-9ad1ed4c]{position:relative;z-index:1;cursor:text;margin-left:var(--input-horizontal-spacing);margin-right:.1764705882rem}@media only screen and (max-width:735px){.filter__filter-button[data-v-9ad1ed4c]{margin-right:.4117647059rem}}.filter__filter-button .svg-icon[data-v-9ad1ed4c]{fill:var(--input-text);display:block;height:21px}.filter__filter-button.blue[data-v-9ad1ed4c]>*{fill:var(--color-figure-blue);color:var(--color-figure-blue)}.filter.focus .filter__wrapper[data-v-9ad1ed4c]{box-shadow:0 0 0 3pt var(--color-focus-color);--input-border-color:var(--color-fill-blue)}.filter__wrapper[data-v-9ad1ed4c]{border:1px solid var(--input-border-color);background:var(--color-fill);border-radius:var(--border-radius,4px)}.filter__wrapper--reversed[data-v-9ad1ed4c]{display:flex;flex-direction:column-reverse}.filter__wrapper--no-border-style[data-v-9ad1ed4c]{border:none}.filter__suggested-tags[data-v-9ad1ed4c]{border-top:1px solid var(--color-fill-gray-tertiary);z-index:1;overflow:hidden}.filter__suggested-tags[data-v-9ad1ed4c] ul{padding:var(--input-vertical-padding) .5294117647rem;border:1px solid transparent;border-bottom-left-radius:calc(var(--border-radius, 4px) - 1px);border-bottom-right-radius:calc(var(--border-radius, 4px) - 1px)}.fromkeyboard .filter__suggested-tags[data-v-9ad1ed4c] ul:focus{outline:none;box-shadow:0 0 0 5px var(--color-focus-color)}.filter__wrapper--reversed .filter__suggested-tags[data-v-9ad1ed4c]{border-bottom:1px solid var(--color-fill-gray-tertiary);border-top:none}.filter__selected-tags[data-v-9ad1ed4c]{z-index:1;padding-left:4px;margin:-4px 0}@media only screen and (max-width:735px){.filter__selected-tags[data-v-9ad1ed4c]{padding-left:0}}.filter__selected-tags[data-v-9ad1ed4c] ul{padding:4px}@media only screen and (max-width:735px){.filter__selected-tags[data-v-9ad1ed4c] ul{padding-right:.4117647059rem}}.filter__selected-tags[data-v-9ad1ed4c] ul .tag:last-child{padding-right:0}.filter__delete-button[data-v-9ad1ed4c]{position:relative;margin:0;z-index:1;border-radius:100%}.fromkeyboard .filter__delete-button[data-v-9ad1ed4c]:focus{box-shadow:0 0 0 4px var(--color-focus-color);outline:none}.filter__delete-button .clear-rounded-icon[data-v-9ad1ed4c]{height:.7058823529rem;width:.7058823529rem;fill:var(--input-text);display:block}.filter__delete-button-wrapper[data-v-9ad1ed4c]{display:flex;align-items:center;padding-right:var(--input-horizontal-spacing);padding-left:.1764705882rem;border-top-right-radius:var(--border-radius,4px);border-bottom-right-radius:var(--border-radius,4px)}.filter__input-label[data-v-9ad1ed4c]{position:relative;flex-grow:1;height:var(--input-height);padding:var(--input-vertical-padding) 0}.filter__input-label[data-v-9ad1ed4c]:after{content:attr(data-value);visibility:hidden;width:auto;white-space:nowrap;min-width:130px;display:block;text-indent:.4117647059rem}@media only screen and (max-width:735px){.filter__input-label[data-v-9ad1ed4c]:after{text-indent:.1764705882rem}}.filter__input-box-wrapper[data-v-9ad1ed4c]{overflow-y:hidden;-ms-overflow-style:none;scrollbar-color:var(--color-figure-gray-tertiary) transparent;scrollbar-width:thin;display:flex;overflow-x:auto;align-items:center;cursor:text;flex:1}.filter__input-box-wrapper[data-v-9ad1ed4c]::-webkit-scrollbar{height:0}@supports not ((-webkit-touch-callout:none) or (scrollbar-width:none) or (-ms-overflow-style:none)){.filter__input-box-wrapper.scrolling[data-v-9ad1ed4c]{--scrollbar-height:11px;padding-top:var(--scrollbar-height);height:calc(var(--scroll-target-height) - var(--scrollbar-height));display:flex;align-items:center}}.filter__input-box-wrapper.scrolling[data-v-9ad1ed4c]::-webkit-scrollbar{height:11px}.filter__input-box-wrapper.scrolling[data-v-9ad1ed4c]::-webkit-scrollbar-thumb{border-radius:10px;background-color:var(--color-figure-gray-tertiary);border:2px solid transparent;background-clip:padding-box}.filter__input-box-wrapper.scrolling[data-v-9ad1ed4c]::-webkit-scrollbar-track-piece:end{margin-right:8px}.filter__input-box-wrapper.scrolling[data-v-9ad1ed4c]::-webkit-scrollbar-track-piece:start{margin-left:8px}.filter__input[data-v-9ad1ed4c]{font-size:1.2352941176rem;line-height:1.380952381;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-text);height:var(--input-height);border:none;width:100%;position:absolute;background:transparent;z-index:1;text-indent:.4117647059rem}@media only screen and (max-width:735px){.filter__input[data-v-9ad1ed4c]{font-size:1.1176470588rem;line-height:1.4210526316;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);text-indent:.1764705882rem}}.filter__input[data-v-9ad1ed4c]:focus{outline:none}.filter__input[placeholder][data-v-9ad1ed4c]::-moz-placeholder{color:var(--input-text);opacity:1}.filter__input[placeholder][data-v-9ad1ed4c]::placeholder{color:var(--input-text);opacity:1}.filter__input[placeholder][data-v-9ad1ed4c]:-ms-input-placeholder{color:var(--input-text)}.filter__input[placeholder][data-v-9ad1ed4c]::-ms-input-placeholder{color:var(--input-text)}.generic-modal[data-v-795f7b59]{position:fixed;top:0;left:0;right:0;bottom:0;margin:0;z-index:11000;display:flex;align-items:center;justify-content:center;flex-wrap:wrap;background:none;overflow:auto}.modal-fullscreen[data-v-795f7b59]{align-items:stretch}.modal-fullscreen .container[data-v-795f7b59]{margin:0;flex:1;width:100%;height:100%;padding-top:env(safe-area-inset-top);padding-right:env(safe-area-inset-right);padding-bottom:env(safe-area-inset-bottom);padding-left:env(safe-area-inset-left)}.modal-standard[data-v-795f7b59]{padding:20px}.modal-standard .container[data-v-795f7b59]{padding:60px;border-radius:var(--border-radius,4px)}@media screen{[data-color-scheme=dark] .modal-standard .container[data-v-795f7b59]{background:#1d1d1f}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .modal-standard .container[data-v-795f7b59]{background:#1d1d1f}}@media only screen and (max-width:735px){.modal-standard[data-v-795f7b59]{padding:0;align-items:stretch}.modal-standard .container[data-v-795f7b59]{margin:20px 0 0;padding:50px 30px;flex:1;width:100%;border-bottom-left-radius:0;border-bottom-right-radius:0}}.backdrop[data-v-795f7b59]{overflow:auto;background:var(--backdrop-background,rgba(0,0,0,.4));-webkit-overflow-scrolling:touch;width:100%;height:100%;position:fixed}.container[data-v-795f7b59]{margin-left:auto;margin-right:auto;width:1536px;width:980px;background:var(--colors-generic-modal-background,var(--color-generic-modal-background));z-index:1;position:relative;overflow:auto;max-width:100%}@media only screen and (max-width:1250px){.container[data-v-795f7b59]{width:692px}}@media only screen and (max-width:735px){.container[data-v-795f7b59]{width:87.5%}}@media only screen and (max-width:320px){.container[data-v-795f7b59]{width:215px}}.close[data-v-795f7b59]{position:absolute;z-index:9999;top:22px;left:22px;width:17px;height:17px;color:#666;cursor:pointer;background:none;border:0;display:flex;align-items:center}.close .close-icon[data-v-795f7b59]{fill:currentColor;width:100%;height:100%}.theme-dark .container[data-v-795f7b59]{background:#000}.theme-dark .container .close[data-v-795f7b59]{color:#b0b0b0}.theme-code .container[data-v-795f7b59]{background-color:var(--code-background,var(--color-code-background))}.highlight[data-v-4a2ce75d]{display:inline}.highlight[data-v-4a2ce75d] .match{font-weight:600;background:var(--color-fill-light-blue-secondary)}@media only screen and (max-width:735px){.preview[data-v-779b8b01]{display:none}}.unavailable[data-v-779b8b01]{align-items:center;display:flex;height:100%;justify-content:center}.loading[data-v-779b8b01]{padding:20px}.loading-row[data-v-779b8b01]{animation:pulse 2.5s ease;animation-delay:calc(1s + .3s*var(--index));animation-fill-mode:forwards;animation-iteration-count:infinite;background-color:var(--color-fill-gray-tertiary);border-radius:4px;height:12px;margin:20px 0;opacity:0}.loading-row[data-v-779b8b01]:first-of-type{margin-top:0}.loading-row[data-v-779b8b01]:last-of-type{margin-bottom:0}.quick-navigation[data-v-2f89fac2]{--input-border-color:var(--color-grid)}.quick-navigation input[type=text][data-v-2f89fac2]{font-size:1.2352941176rem;line-height:1.380952381;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:735px){.quick-navigation input[type=text][data-v-2f89fac2]{font-size:1.1176470588rem;line-height:1.4210526316;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.quick-navigation__filter[data-v-2f89fac2]{--input-horizontal-spacing:0.8823529412rem}.quick-navigation[data-v-2f89fac2] .filter__wrapper{background-color:var(--color-fill-secondary)}.quick-navigation__container[data-v-2f89fac2]{background-color:var(--color-fill-secondary);border:solid 1px var(--input-border-color);border-radius:var(--border-radius,4px);margin:0 .9411764706rem}.quick-navigation__container>[data-v-2f89fac2]{--input-text:var(--color-figure-gray-secondary)}.quick-navigation__container.focus[data-v-2f89fac2]{box-shadow:0 0 0 4px var(--color-focus-color);outline:none;border-color:var(--color-focus-border-color)}.quick-navigation__container[data-v-2f89fac2] .declaration-list-toggle{background-color:var(--color-fill-secondary)}.quick-navigation__magnifier-icon-container[data-v-2f89fac2]{width:1rem}.quick-navigation__magnifier-icon-container>[data-v-2f89fac2]{width:100%}.quick-navigation__magnifier-icon-container.blue .magnifier-icon[data-v-2f89fac2]{fill:var(--color-figure-blue);color:var(--color-figure-blue)}.quick-navigation__match-list[data-v-2f89fac2]{display:flex;max-height:26.4705882353rem;height:0}.quick-navigation__match-list>[data-v-2f89fac2]{min-width:0}.quick-navigation__match-list.active[data-v-2f89fac2]{height:auto;border-top:1px solid var(--input-border-color)}.quick-navigation__match-list .no-results[data-v-2f89fac2]{margin:.8823529412rem auto;width:-moz-fit-content;width:fit-content}.quick-navigation__refs[data-v-2f89fac2]{flex:1;overflow:auto}.quick-navigation__preview[data-v-2f89fac2]{border-left:1px solid var(--color-grid);flex:0 0 61.8%;overflow:auto;position:sticky;top:0}.quick-navigation__reference[data-v-2f89fac2]{display:block;padding:.5882352941rem .8823529412rem}.quick-navigation__reference[data-v-2f89fac2]:hover{text-decoration:none;background-color:var(--color-navigator-item-hover)}.quick-navigation__reference[data-v-2f89fac2]:focus{margin:0 .2941176471rem;padding:.5882352941rem .5882352941rem;background-color:var(--color-navigator-item-hover)}.quick-navigation__symbol-match[data-v-2f89fac2]{display:flex;height:2.3529411765rem;color:var(--color-figure-gray)}.quick-navigation__symbol-match .symbol-info[data-v-2f89fac2]{margin:auto;width:100%}.quick-navigation__symbol-match .symbol-info .navigator-icon[data-v-2f89fac2]{margin-right:.5882352941rem}.quick-navigation__symbol-match .symbol-info .symbol-name[data-v-2f89fac2]{display:flex}.quick-navigation__symbol-match .symbol-info .symbol-name .symbol-title[data-v-2f89fac2]{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.quick-navigation__symbol-match .symbol-info .symbol-path[data-v-2f89fac2]{font-size:.8235294118rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-figure-gray-secondary);display:flex;margin-left:1.5882352941rem;overflow:hidden;white-space:nowrap}.quick-navigation__symbol-match .symbol-info .symbol-path .parent-path[data-v-2f89fac2]{padding-right:.2941176471rem}@media print{.sidebar[data-v-1a55f7f5]{display:none}}.adjustable-sidebar-width[data-v-1a55f7f5]{display:flex}@media only screen and (max-width:1023px){.adjustable-sidebar-width[data-v-1a55f7f5]{display:block;position:relative}}.adjustable-sidebar-width.dragging[data-v-1a55f7f5] *{cursor:col-resize!important}.adjustable-sidebar-width.sidebar-hidden.dragging[data-v-1a55f7f5] *{cursor:e-resize!important}.sidebar[data-v-1a55f7f5]{position:relative}@media only screen and (max-width:1023px){.sidebar[data-v-1a55f7f5]{position:static}}.aside[data-v-1a55f7f5]{width:250px;position:relative;height:100%;max-width:100vw}.aside.no-transition[data-v-1a55f7f5]{transition:none!important}@media only screen and (min-width:1024px){.aside[data-v-1a55f7f5]{transition:width .3s ease-in,visibility 0s linear var(--visibility-transition-time,0s)}.aside.dragging[data-v-1a55f7f5]:not(.is-opening-on-large):not(.hide-on-large){transition:none}.aside.hide-on-large[data-v-1a55f7f5]{width:0!important;visibility:hidden;pointer-events:none;--visibility-transition-time:0.3s}}@media only screen and (max-width:1023px){.aside[data-v-1a55f7f5]{width:100%!important;overflow:hidden;min-width:0;max-width:100%;height:calc(var(--app-height) - var(--top-offset-mobile));position:fixed;top:var(--top-offset-mobile);bottom:0;z-index:9998;transform:translateX(-100%);transition:transform .15s ease-in;left:0}.aside[data-v-1a55f7f5] .aside-animated-child{opacity:0}.aside.show-on-mobile[data-v-1a55f7f5]{transform:translateX(0)}.aside.show-on-mobile[data-v-1a55f7f5] .aside-animated-child{--index:0;opacity:1;transition:opacity .15s linear;transition-delay:calc(var(--index)*.15s + .15s)}.aside.has-mobile-top-offset[data-v-1a55f7f5]{border-top:1px solid var(--color-fill-gray-tertiary)}}.content[data-v-1a55f7f5]{display:flex;flex-flow:column;min-width:0;flex:1 1 auto;height:100%}.resize-handle[data-v-1a55f7f5]{position:absolute;cursor:col-resize;top:0;bottom:0;right:0;width:5px;height:100%;-webkit-user-select:none;-moz-user-select:none;user-select:none;z-index:1;transition:background-color .15s;transform:translateX(50%)}@media only screen and (max-width:1023px){.resize-handle[data-v-1a55f7f5]{display:none}}.resize-handle[data-v-1a55f7f5]:hover{background:var(--color-fill-gray-tertiary)}.navigator-card-item[data-v-5e71f320]{--nav-head-wrapper-left-space:20px;--nav-head-wrapper-right-space:20px;--head-wrapper-vertical-space:5px;--nav-depth-spacer:20px;--nesting-index:0;display:flex;align-items:stretch;min-height:32px;box-sizing:border-box;padding:0 var(--nav-head-wrapper-right-space) 0 var(--nav-head-wrapper-left-space)}.navigator-card-item.active .head-wrapper[data-v-5e71f320]{background:var(--color-fill-gray-quaternary)}.hover .navigator-card-item:not(.is-group) .head-wrapper[data-v-5e71f320]{background:var(--color-navigator-item-hover)}.depth-spacer[data-v-5e71f320]{width:calc(var(--nesting-index)*15px + var(--nav-depth-spacer));height:100%;position:relative;flex:0 0 auto}.title-container[data-v-5e71f320]{width:100%;min-width:0;display:flex;align-items:center}.navigator-icon-wrapper[data-v-5e71f320]{margin-right:7px}.head-wrapper[data-v-5e71f320]{position:relative;display:flex;align-items:center;flex:1;min-width:0;border-radius:var(--border-radius,4px);padding:var(--head-wrapper-vertical-space) 0}.fromkeyboard .head-wrapper[data-v-5e71f320]:focus-within{outline:4px solid var(--color-focus-color);outline-offset:-4px}@supports(padding:max(0px)){.head-wrapper[data-v-5e71f320]{padding-right:max(var(--nav-head-wrapper-right-space),env(safe-area-inset-right))}}.highlight[data-v-7b81ca08]{display:inline}.highlight[data-v-7b81ca08] .match{font-weight:600;background:var(--color-fill-light-blue-secondary)}.is-group .leaf-link[data-v-5148de22]{color:var(--color-figure-gray-tertiary);font-weight:600}.is-group .leaf-link[data-v-5148de22]:after{display:none}.navigator-icon[data-v-5148de22]{display:flex;flex:0 0 auto}.navigator-icon.changed[data-v-5148de22]{border:none;width:1em;height:1em;z-index:0}.navigator-icon.changed[data-v-5148de22]:after{top:50%;left:50%;right:auto;bottom:auto;transform:translate(-50%,-50%);background-image:url(../img/modified-icon.efb2697d.svg);margin:0}@media screen{[data-color-scheme=dark] .navigator-icon.changed[data-v-5148de22]:after{background-image:url(../img/modified-icon.efb2697d.svg)}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .navigator-icon.changed[data-v-5148de22]:after{background-image:url(../img/modified-icon.efb2697d.svg)}}.navigator-icon.changed-added[data-v-5148de22]:after{background-image:url(../img/added-icon.832a5d2c.svg)}@media screen{[data-color-scheme=dark] .navigator-icon.changed-added[data-v-5148de22]:after{background-image:url(../img/added-icon.832a5d2c.svg)}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .navigator-icon.changed-added[data-v-5148de22]:after{background-image:url(../img/added-icon.832a5d2c.svg)}}.navigator-icon.changed-deprecated[data-v-5148de22]:after{background-image:url(../img/deprecated-icon.7bf1740a.svg)}@media screen{[data-color-scheme=dark] .navigator-icon.changed-deprecated[data-v-5148de22]:after{background-image:url(../img/deprecated-icon.7bf1740a.svg)}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .navigator-icon.changed-deprecated[data-v-5148de22]:after{background-image:url(../img/deprecated-icon.7bf1740a.svg)}}.leaf-link[data-v-5148de22]{color:var(--color-figure-gray);text-overflow:ellipsis;overflow:hidden;white-space:nowrap;max-width:100%;display:inline;vertical-align:middle;font-size:1rem;line-height:1.4705882353;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.fromkeyboard .leaf-link[data-v-5148de22]:focus{outline:none}.leaf-link[data-v-5148de22]:hover{text-decoration:none}.leaf-link.bolded[data-v-5148de22]{font-weight:600}.leaf-link[data-v-5148de22]:after{content:"";position:absolute;top:0;left:0;right:0;bottom:0}.extended-content[data-v-5148de22]{font-size:.8235294118rem;line-height:1.4285714286;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-figure-gray-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.tree-toggle[data-v-5148de22]{overflow:hidden;position:absolute;width:100%;height:100%;padding-right:5px;box-sizing:border-box;z-index:1;display:flex;align-items:center;justify-content:flex-end}.chevron[data-v-5148de22]{width:10px}.chevron.animating[data-v-5148de22]{transition:transform .15s ease-in}.chevron.rotate[data-v-5148de22]{transform:rotate(90deg)}.navigator-card[data-v-584a744a]{--card-vertical-spacing:10px;--card-horizontal-spacing:20px;--nav-filter-horizontal-padding:20px;--visibility-delay:1s;display:flex;flex-direction:column;min-height:0;height:calc(var(--app-height) - 3.05882rem);position:sticky;top:3.0588235294rem}@media only screen and (max-width:1023px){.navigator-card[data-v-584a744a]{height:100%;position:static;background:var(--color-fill)}}.navigator-card .navigator-card-full-height[data-v-584a744a]{min-height:0;flex:1 1 auto}.navigator-card .head-inner[data-v-584a744a]{display:none;width:100%;font-size:1.2352941176rem;line-height:1.1904761905;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);height:2.8235294118rem}@media only screen and (max-width:767px){.navigator-card .head-inner[data-v-584a744a]{font-size:1rem;line-height:1;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:1023px){.navigator-card .head-inner[data-v-584a744a]{display:flex;justify-content:flex-end;align-items:center}}.navigator-card .head-inner span[data-v-584a744a],.navigator-card .head-inner>a[data-v-584a744a]{color:var(--color-figure-gray);width:100%}.navigator-card .head-wrapper[data-v-584a744a]{display:flex;justify-content:space-between;flex:1 0 auto}@supports(padding:max(0px)){.navigator-card .head-wrapper[data-v-584a744a]{margin-left:max(var(--card-horizontal-spacing),env(safe-area-inset-left));margin-right:max(var(--card-horizontal-spacing),env(safe-area-inset-right))}}.close-card[data-v-584a744a]{margin:0}.close-card .close-icon[data-v-584a744a]{width:19px;height:19px}[data-v-584a744a] .card-body{display:flex;flex-direction:column;padding-right:0;flex:1 1 auto;min-height:0;height:100%}@media only screen and (max-width:1023px){[data-v-584a744a] .card-body{--card-vertical-spacing:0px}}.navigator-card-inner[data-v-584a744a]{display:flex;flex-flow:column;height:100%;padding-top:10px;box-sizing:border-box}@media only screen and (max-width:1023px){.navigator-card-inner[data-v-584a744a]{padding-top:0}}.filter-on-top .navigator-card-inner[data-v-584a744a]{padding-top:0}.vue-recycle-scroller{position:relative}.vue-recycle-scroller.direction-vertical:not(.page-mode){overflow-y:auto}.vue-recycle-scroller.direction-horizontal:not(.page-mode){overflow-x:auto}.vue-recycle-scroller.direction-horizontal{display:-webkit-box;display:-ms-flexbox;display:flex}.vue-recycle-scroller__slot{-webkit-box-flex:1;-ms-flex:auto 0 0px;flex:auto 0 0}.vue-recycle-scroller__item-wrapper{-webkit-box-flex:1;-ms-flex:1;flex:1;-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;position:relative}.vue-recycle-scroller.ready .vue-recycle-scroller__item-view{position:absolute;top:0;left:0;will-change:transform}.vue-recycle-scroller.direction-vertical .vue-recycle-scroller__item-wrapper{width:100%}.vue-recycle-scroller.direction-horizontal .vue-recycle-scroller__item-wrapper{height:100%}.vue-recycle-scroller.ready.direction-vertical .vue-recycle-scroller__item-view{width:100%}.vue-recycle-scroller.ready.direction-horizontal .vue-recycle-scroller__item-view{height:100%}.resize-observer[data-v-b329ee4c]{border:none;background-color:transparent;opacity:0}.resize-observer[data-v-b329ee4c],.resize-observer[data-v-b329ee4c] object{position:absolute;top:0;left:0;z-index:-1;width:100%;height:100%;pointer-events:none;display:block;overflow:hidden}.navigator-card.filter-on-top .filter-wrapper[data-v-4fc101dd]{order:1;position:static}.navigator-card.filter-on-top .card-body[data-v-4fc101dd]{order:2}.no-items-wrapper[data-v-4fc101dd]{overflow:hidden;color:var(--color-figure-gray-tertiary)}.no-items-wrapper .no-items[data-v-4fc101dd]:not(:empty){font-size:.8235294118rem;line-height:1.4285714286;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);padding:var(--card-vertical-spacing) var(--card-horizontal-spacing);min-width:200px;box-sizing:border-box}.technology-title[data-v-4fc101dd]{padding:8px 10px;padding-left:20px;background:var(--color-fill);border-radius:var(--border-radius,4px);display:flex;white-space:nowrap}@supports(padding:max(0px)){.technology-title[data-v-4fc101dd]{margin-left:max(var(--card-horizontal-spacing),env(safe-area-inset-left));margin-right:max(var(--card-horizontal-spacing),env(safe-area-inset-right))}}@media only screen and (max-width:767px){.technology-title[data-v-4fc101dd]{margin-top:0}}.technology-title .card-link[data-v-4fc101dd]{color:var(--color-text);font-size:1.1176470588rem;line-height:1.2105263158;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);font-weight:600;overflow:hidden;text-overflow:ellipsis}.technology-title.router-link-exact-active[data-v-4fc101dd]{background:var(--color-fill-gray-quaternary)}.technology-title[data-v-4fc101dd]:hover{background:var(--color-navigator-item-hover);text-decoration:none}.navigator-filter[data-v-4fc101dd]{box-sizing:border-box;padding:15px var(--nav-filter-horizontal-padding);border-top:1px solid var(--color-grid);height:71px;display:flex;align-items:flex-end}.filter-on-top .navigator-filter[data-v-4fc101dd]{border-top:none;align-items:flex-start}@supports(padding:max(0px)){.navigator-filter[data-v-4fc101dd]{padding-left:max(var(--nav-filter-horizontal-padding),env(safe-area-inset-left));padding-right:max(var(--nav-filter-horizontal-padding),env(safe-area-inset-right))}}@media only screen and (max-width:1023px){.navigator-filter[data-v-4fc101dd]{--nav-filter-horizontal-padding:20px;border:none;padding-top:10px;padding-bottom:10px;height:60px}}.navigator-filter .input-wrapper[data-v-4fc101dd]{position:relative;flex:1;min-width:0}.navigator-filter .filter-component[data-v-4fc101dd]{--input-vertical-padding:8px;--input-height:22px;--input-border-color:var(--color-grid);--input-text:var(--color-figure-gray-secondary)}.navigator-filter .filter-component[data-v-4fc101dd] .filter__input{font-size:1rem;line-height:1.4705882353;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.navigator-filter .filter-component[data-v-4fc101dd] .filter__input-label:after{min-width:70px}.scroller[data-v-4fc101dd]{height:100%;box-sizing:border-box;padding-bottom:calc(var(--top-offset, 0px) + var(--card-vertical-spacing));transition:padding-bottom .15s ease-in}@media only screen and (max-width:1023px){.scroller[data-v-4fc101dd]{padding-bottom:10em}}.scroller[data-v-4fc101dd] .vue-recycle-scroller__item-wrapper{transform:translateZ(0)}.filter-wrapper[data-v-4fc101dd]{position:sticky;bottom:0;background:var(--color-fill)}.sidebar-transitioning .filter-wrapper[data-v-4fc101dd]{flex:1 0 71px;overflow:hidden}@media only screen and (max-width:1023px){.sidebar-transitioning .filter-wrapper[data-v-4fc101dd]{flex-basis:60px}}.loader[data-v-0de29914]{height:.7058823529rem;background-color:var(--color-fill-gray-tertiary);border-radius:4px}.navigator-icon[data-v-0de29914]{width:16px;height:16px;border-radius:2px;background-color:var(--color-fill-gray-tertiary)}.loading-navigator-item[data-v-0de29914]{animation:pulse 2.5s ease;animation-iteration-count:infinite;animation-fill-mode:forwards;opacity:0;animation-delay:calc(var(--visibility-delay) + .3s*var(--index))}.delay-visibility-enter-active[data-v-3b7cf3a4]{transition:visibility var(--visibility-delay);visibility:hidden}.loading-navigator[data-v-3b7cf3a4]{padding-top:10px}.navigator[data-v-7c66a058]{height:100%;display:flex;flex-flow:column}@media only screen and (max-width:1023px){.navigator[data-v-7c66a058]{position:static;transition:none}}[data-v-7c66a058] .nav-title{font-size:inherit;font-weight:inherit;flex-grow:1}.nav-menu-setting-label[data-v-4323807e]{display:inline-block;margin-right:.2941176471rem;white-space:nowrap}.language-container[data-v-4323807e]{flex:1 0 auto}.language-dropdown[data-v-4323807e]{-webkit-text-size-adjust:none;-webkit-appearance:none;-moz-appearance:none;appearance:none;border:none;background-color:transparent;box-sizing:inherit;padding:0 11px 0 4px;margin-left:-4px;font-size:.8235294118rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);cursor:pointer;position:relative;z-index:1}@media only screen and (max-width:1023px){.language-dropdown[data-v-4323807e]{font-size:.8235294118rem;line-height:1.5;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.language-dropdown[data-v-4323807e]:focus{outline:none}.fromkeyboard .language-dropdown[data-v-4323807e]:focus{outline:4px solid var(--color-focus-color);outline-offset:1px}.language-sizer[data-v-4323807e]{position:absolute;opacity:0;pointer-events:none;padding:0}.language-toggle-container[data-v-4323807e]{display:flex;align-items:center;padding-right:.1764705882rem;position:relative}.nav--in-breakpoint-range .language-toggle-container[data-v-4323807e]{display:none}.language-toggle-container .toggle-icon[data-v-4323807e]{width:.6em;height:.6em;position:absolute;right:7px}.language-toggle-label[data-v-4323807e]{margin-right:2px}.language-toggle.nav-menu-toggle-label[data-v-4323807e]{margin-right:6px}.language-list[data-v-4323807e]{display:inline-block;margin-top:0}.language-list-container[data-v-4323807e]{display:none}.language-list-item[data-v-4323807e],.nav--in-breakpoint-range .language-list-container[data-v-4323807e]{display:inline-block}.language-list-item[data-v-4323807e]:not(:first-child){border-left:1px solid var(--color-grid);margin-left:6px;padding-left:6px}[data-v-5e58283e] .nav-menu{line-height:1.5}[data-v-5e58283e] .nav-menu,[data-v-5e58283e] .nav-menu-settings{font-size:.8235294118rem;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}[data-v-5e58283e] .nav-menu-settings{min-width:0;line-height:1.2857142857}@media only screen and (max-width:1023px){[data-v-5e58283e] .nav-menu-settings{font-size:.8235294118rem;line-height:1.5;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (min-width:1024px){[data-v-5e58283e] .nav-menu-settings{margin-left:.5882352941rem}}[data-v-5e58283e] .nav-menu-settings .nav-menu-setting{display:flex;align-items:center;color:var(--color-nav-current-link);margin-left:0;min-width:0}[data-v-5e58283e] .nav-menu-settings .nav-menu-setting .nav-menu-link{font-weight:600;text-decoration:underline}[data-v-5e58283e] .nav-menu-settings .nav-menu-setting:first-child:not(:only-child){margin-right:.5882352941rem}.nav--in-breakpoint-range[data-v-5e58283e] .nav-menu-settings .nav-menu-setting:first-child:not(:only-child){margin-right:0}.theme-dark[data-v-5e58283e] .nav-menu-settings .nav-menu-setting{color:var(--color-nav-dark-current-link)}.nav--in-breakpoint-range[data-v-5e58283e] .nav-menu-settings .nav-menu-setting:not(:first-child){border-top:1px solid var(--color-fill-gray-tertiary)}.documentation-nav[data-v-5e58283e]{--color-nav-background:var(--color-fill)}.documentation-nav[data-v-5e58283e] .nav-title{font-size:1.2352941176rem;line-height:1.1904761905;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:767px){.documentation-nav[data-v-5e58283e] .nav-title{font-size:1rem;line-height:1;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.sidenav-toggle-wrapper[data-v-5e58283e]{display:flex;margin-top:1px;margin-right:.6470588235rem}.nav--in-breakpoint-range .sidenav-toggle-wrapper[data-v-5e58283e]{display:flex!important}@media only screen and (min-width:1024px){.sidenav-toggle-enter-active[data-v-5e58283e],.sidenav-toggle-leave-active[data-v-5e58283e]{transition:margin .3s ease-in 0s}.sidenav-toggle-enter[data-v-5e58283e],.sidenav-toggle-leave-to[data-v-5e58283e]{margin-left:-3.7647058824rem}}.sidenav-toggle[data-v-5e58283e]{align-self:center;color:var(--color-nav-link-color);position:relative;margin:0 -5px;border-radius:var(--border-radius,4px)}.theme-dark .sidenav-toggle[data-v-5e58283e]{color:var(--color-nav-dark-link-color)}.sidenav-toggle:hover .sidenav-icon-wrapper[data-v-5e58283e]{background:var(--color-fill-gray-quaternary)}.theme-dark .sidenav-toggle:hover .sidenav-icon-wrapper[data-v-5e58283e]{background:#424242}.sidenav-toggle__separator[data-v-5e58283e]{height:.8em;width:1px;background:var(--color-nav-color);align-self:center;margin:0 1.2941176471rem}.nav--in-breakpoint-range .sidenav-toggle__separator[data-v-5e58283e]{display:none}.sidenav-icon-wrapper[data-v-5e58283e]{padding:5px;display:flex;align-items:center;justify-content:center;border-radius:var(--border-radius,4px)}.sidenav-icon[data-v-5e58283e]{display:flex;width:19px;height:19px}[data-v-8aa6db48] .generic-modal{overflow-y:overlay}[data-v-8aa6db48] .modal-fullscreen>.container{background-color:transparent;height:-moz-fit-content;height:fit-content;flex:auto;margin:9.4117647059rem 0;max-width:47.0588235294rem;overflow:visible}[data-v-8aa6db48] .navigator-filter .quick-navigation-open{margin-left:var(--nav-filter-horizontal-padding);width:calc(var(--nav-filter-horizontal-padding)*2)}.documentation-layout[data-v-8aa6db48]{--delay:1s;display:flex;flex-flow:column;background:var(--colors-text-background,var(--color-text-background))}.documentation-layout .delay-hiding-leave-active[data-v-8aa6db48]{transition:display var(--delay)}.documentation-layout-aside[data-v-8aa6db48]{height:100%;box-sizing:border-box;border-right:1px solid var(--color-grid)}@media only screen and (max-width:1023px){.documentation-layout-aside[data-v-8aa6db48]{background:var(--color-fill);border-right:none}.sidebar-transitioning .documentation-layout-aside[data-v-8aa6db48]{border-right:1px solid var(--color-grid)}}.topic-wrapper[data-v-8aa6db48]{flex:1 1 auto;width:100%}:root.no-js .topic-wrapper[data-v-8aa6db48] .sidebar{display:none}.full-width-container[data-v-8aa6db48]{max-width:1920px;margin-left:auto;margin-right:auto}@media only screen and (min-width:1920px){.full-width-container[data-v-8aa6db48]{border-left:1px solid var(--color-grid);border-right:1px solid var(--color-grid);box-sizing:border-box}} \ No newline at end of file diff --git a/docs/css/index.95ac671e.css b/docs/css/index.95ac671e.css new file mode 100644 index 0000000..e786b3e --- /dev/null +++ b/docs/css/index.95ac671e.css @@ -0,0 +1,9 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */.color-scheme-toggle[data-v-0c0360ce]{--toggle-color-fill:var(--color-button-background);--toggle-color-text:var(--color-fill-blue);font-size:.7058823529rem;line-height:1.3333333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);border:1px solid var(--toggle-color-fill);border-radius:var(--toggle-border-radius-outer,var(--border-radius,4px));display:inline-flex;padding:1px}@media screen{[data-color-scheme=dark] .color-scheme-toggle[data-v-0c0360ce]{--toggle-color-text:var(--color-figure-blue)}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .color-scheme-toggle[data-v-0c0360ce]{--toggle-color-text:var(--color-figure-blue)}}@media print{.color-scheme-toggle[data-v-0c0360ce]{display:none}}:root.no-js .color-scheme-toggle[data-v-0c0360ce]{visibility:hidden}input[data-v-0c0360ce]{position:absolute;clip:rect(1px,1px,1px,1px);-webkit-clip-path:inset(0 0 99.9% 99.9%);clip-path:inset(0 0 99.9% 99.9%);overflow:hidden;height:1px;width:1px;padding:0;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}.fromkeyboard label[data-v-0c0360ce]:focus-within{outline:4px solid var(--color-focus-color);outline-offset:1px}.text[data-v-0c0360ce]{border:1px solid transparent;border-radius:var(--toggle-border-radius-inner,2px);color:var(--toggle-color-text);display:inline-block;text-align:center;padding:1px 6px;min-width:42px;box-sizing:border-box}.text[data-v-0c0360ce]:hover{cursor:pointer}input:checked+.text[data-v-0c0360ce]{--toggle-color-text:var(--color-button-text);background:var(--toggle-color-fill);border-color:var(--toggle-color-fill)}.footer[data-v-f1d65b2a]{border-top:1px solid var(--color-grid)}.row[data-v-f1d65b2a]{margin-left:auto;margin-right:auto;width:1536px;width:980px;display:flex;flex-direction:row-reverse;margin:20px auto}@media only screen and (max-width:1250px){.row[data-v-f1d65b2a]{width:692px}}@media only screen and (max-width:735px){.row[data-v-f1d65b2a]{width:87.5%}}@media only screen and (max-width:320px){.row[data-v-f1d65b2a]{width:215px}}@media only screen and (max-width:735px){.row[data-v-f1d65b2a]{width:100%;padding:0 .9411764706rem;box-sizing:border-box}}.InitialLoadingPlaceholder[data-v-35c356b6]{background:var(--colors-loading-placeholder-background,var(--color-loading-placeholder-background));height:100vh;width:100%}.svg-icon[data-v-3434f4d2]{fill:var(--colors-svg-icon-fill-light,var(--color-svg-icon));transform:scale(1);-webkit-transform:scale(1);overflow:visible}.theme-dark .svg-icon[data-v-3434f4d2]{fill:var(--colors-svg-icon-fill-dark,var(--color-svg-icon))}@media screen{[data-color-scheme=dark] .svg-icon[data-v-3434f4d2]{fill:var(--colors-svg-icon-fill-dark,var(--color-svg-icon))}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .svg-icon[data-v-3434f4d2]{fill:var(--colors-svg-icon-fill-dark,var(--color-svg-icon))}}.svg-icon.icon-inline[data-v-3434f4d2]{display:inline-block;vertical-align:middle;fill:currentColor}.svg-icon.icon-inline[data-v-3434f4d2] .svg-icon-stroke{stroke:currentColor}[data-v-3434f4d2] .svg-icon-stroke{stroke:var(--colors-svg-icon-fill-light,var(--color-svg-icon))}.theme-dark[data-v-3434f4d2] .svg-icon-stroke{stroke:var(--colors-svg-icon-fill-dark,var(--color-svg-icon))}@media screen{[data-color-scheme=dark][data-v-3434f4d2] .svg-icon-stroke{stroke:var(--colors-svg-icon-fill-dark,var(--color-svg-icon))}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto][data-v-3434f4d2] .svg-icon-stroke{stroke:var(--colors-svg-icon-fill-dark,var(--color-svg-icon))}}.suggest-lang[data-v-c2dca0ae]{background:#000;color:#fff;display:flex;justify-content:center;border-bottom:1px solid var(--color-grid)}.suggest-lang__wrapper[data-v-c2dca0ae]{display:flex;align-items:center;width:100%;max-width:var(--wrapper-max-width,1920px);margin:0 .9411764706rem;position:relative;height:52px}.suggest-lang__link[data-v-c2dca0ae]{font-size:.8235294118rem;line-height:1.4285714286;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);margin:0 auto;color:#09f}.suggest-lang__close-icon-wrapper[data-v-c2dca0ae]{position:absolute;right:-.2352941176rem;top:0;height:100%;box-sizing:border-box;display:flex;align-items:center;z-index:1}.suggest-lang__close-icon-button[data-v-c2dca0ae]{padding:.2352941176rem}.suggest-lang__close-icon-button .close-icon[data-v-c2dca0ae]{width:8px;display:block}.suggest-lang .inline-chevron-right-icon[data-v-c2dca0ae]{padding-left:.2352941176rem;width:8px}select[data-v-d21858a2]{font-size:.7058823529rem;line-height:1.3333333333;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-fill-blue);padding-right:15px;-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;border:none;cursor:pointer}select[data-v-d21858a2]:hover{text-decoration:underline}.locale-selector[data-v-d21858a2]{position:relative}.svg-icon.icon-inline[data-v-d21858a2]{position:absolute;fill:var(--color-fill-blue);right:2px;bottom:7px;height:5px}html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;background-color:var(--colors-text-background,var(--color-text-background));height:100%}abbr,blockquote,body,button,dd,dl,dt,fieldset,figure,form,h1,h2,h3,h4,h5,h6,hgroup,input,legend,li,ol,p,pre,ul{margin:0;padding:0}address,caption,code,figcaption,pre,th{font-size:1em;font-weight:400;font-style:normal}fieldset,iframe,img{border:0}caption,th{text-align:left}table{border-collapse:collapse;border-spacing:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}button{background:none;border:0;box-sizing:content-box;color:inherit;cursor:pointer;font:inherit;line-height:inherit;overflow:visible;vertical-align:inherit}button:disabled{cursor:default}:focus{outline:4px solid var(--color-focus-color);outline-offset:1px}::-moz-focus-inner{border:0;padding:0}@media print{#content,#main,body{color:#000}a,a:link,a:visited{color:#000;text-decoration:none}.hide,.noprint{display:none}}body{height:100%;min-width:320px}html{font:var(--typography-html-font,17px "Helvetica Neue","Helvetica","Arial",sans-serif);quotes:"“" "”"}html:lang(ja-JP){quotes:"「" "」"}body{font-size:1rem;line-height:1.4705882353;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);background-color:var(--color-text-background);color:var(--colors-text,var(--color-text));font-style:normal;word-wrap:break-word;--spacing-stacked-margin-small:0.4em;--spacing-stacked-margin-large:0.8em;--spacing-stacked-margin-xlarge:calc(var(--spacing-stacked-margin-large)*2);--spacing-param:1.6470588235rem;--declaration-code-listing-margin:30px 0 0 0;--code-block-style-elements-padding:8px 14px}body,button,input,select,textarea{font-synthesis:none;-moz-font-feature-settings:"kern";-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;direction:ltr;text-align:left}h1,h2,h3,h4,h5,h6{color:var(--colors-header-text,var(--color-header-text))}h1+*,h2+*,h3+*,h4+*,h5+*,h6+*{margin-top:var(--spacing-stacked-margin-large)}ol+h1,ol+h2,ol+h3,ol+h4,ol+h5,ol+h6,p+h1,p+h2,p+h3,p+h4,p+h5,p+h6,ul+h1,ul+h2,ul+h3,ul+h4,ul+h5,ul+h6{margin-top:1.6em}ol+*,p+*,ul+*{margin-top:var(--spacing-stacked-margin-large)}ol,ul{margin-left:1.1764705882em}ol ol,ol ul,ul ol,ul ul{margin-top:0;margin-bottom:0}nav ol,nav ul{margin:0;list-style:none}li li{font-size:1em}a{color:var(--colors-link,var(--color-link))}a:link,a:visited{text-decoration:none}a.inline-link,a:hover{text-decoration:underline}a:active{text-decoration:none}p+a{display:inline-block}b,strong{font-weight:600}cite,dfn,em,i{font-style:italic}sup{font-size:.6em;vertical-align:top;position:relative;bottom:-.2em}h1 sup,h2 sup,h3 sup{font-size:.4em}sup a{vertical-align:inherit;color:inherit}sup a:hover{color:var(--figure-blue);text-decoration:none}sub{line-height:1}abbr{border:0}pre{overflow:auto;-webkit-overflow-scrolling:auto;white-space:pre;word-wrap:normal}code{font-family:var(--typography-html-font-mono,Menlo,monospace);font-weight:inherit;letter-spacing:0}.syntax-addition{color:var(--syntax-addition,var(--color-syntax-addition))}.syntax-comment{color:var(--syntax-comment,var(--color-syntax-comments))}.syntax-quote{color:var(--syntax-quote,var(--color-syntax-comments))}.syntax-deletion{color:var(--syntax-deletion,var(--color-syntax-deletion))}.syntax-keyword{color:var(--syntax-keyword,var(--color-syntax-keywords))}.syntax-literal{color:var(--syntax-literal,var(--color-syntax-keywords))}.syntax-selector-tag{color:var(--syntax-selector-tag,var(--color-syntax-keywords))}.syntax-string{color:var(--syntax-string,var(--color-syntax-strings))}.syntax-bullet{color:var(--syntax-bullet,var(--color-syntax-characters))}.syntax-meta{color:var(--syntax-meta,var(--color-syntax-characters))}.syntax-number{color:var(--syntax-number,var(--color-syntax-characters))}.syntax-symbol{color:var(--syntax-symbol,var(--color-syntax-characters))}.syntax-tag{color:var(--syntax-tag,var(--color-syntax-characters))}.syntax-attr{color:var(--syntax-attr,var(--color-syntax-other-type-names))}.syntax-built_in{color:var(--syntax-built_in,var(--color-syntax-other-type-names))}.syntax-builtin-name{color:var(--syntax-builtin-name,var(--color-syntax-other-type-names))}.syntax-class{color:var(--syntax-class,var(--color-syntax-other-type-names))}.syntax-params{color:var(--syntax-params,var(--color-syntax-other-type-names))}.syntax-section{color:var(--syntax-section,var(--color-syntax-other-type-names))}.syntax-title{color:var(--syntax-title,var(--color-syntax-other-type-names))}.syntax-type{color:var(--syntax-type,var(--color-syntax-other-type-names))}.syntax-attribute{color:var(--syntax-attribute,var(--color-syntax-plain-text))}.syntax-identifier{color:var(--syntax-identifier,var(--color-syntax-plain-text))}.syntax-subst{color:var(--syntax-subst,var(--color-syntax-plain-text))}.syntax-doctag,.syntax-strong{font-weight:700}.syntax-emphasis,.syntax-link{font-style:italic}[data-syntax=swift] .syntax-meta{color:var(--syntax-meta,var(--color-syntax-keywords))}[data-syntax=swift] .syntax-class,[data-syntax=swift] .syntax-keyword+.syntax-params,[data-syntax=swift] .syntax-params+.syntax-params{color:unset}[data-syntax=json] .syntax-attr{color:var(--syntax-attr,var(--color-syntax-strings))}#skip-nav{position:absolute;clip:rect(1px,1px,1px,1px);-webkit-clip-path:inset(0 0 99.9% 99.9%);clip-path:inset(0 0 99.9% 99.9%);overflow:hidden;height:1px;width:1px;padding:0;border:0}#skip-nav:active,#skip-nav:focus{position:relative;float:left;width:-moz-fit-content;width:fit-content;color:var(--color-figure-blue);font-size:1em;padding:0 10px;z-index:100000;top:0;left:0;height:44px;line-height:44px;-webkit-clip-path:unset;clip-path:unset}.nav--in-breakpoint-range #skip-nav{display:none}.visuallyhidden{position:absolute;clip:rect(1px,1px,1px,1px);-webkit-clip-path:inset(0 0 99.9% 99.9%);clip-path:inset(0 0 99.9% 99.9%);overflow:hidden;height:1px;width:1px;padding:0;border:0}@keyframes pulse{0%{opacity:0}33%{opacity:1}66%{opacity:1}to{opacity:0}}.changed{border:1px solid var(--color-changes-modified);position:relative}.changed,.changed.displays-multiple-lines,.displays-multiple-lines .changed{border-radius:var(--border-radius,4px)}.changed:after{left:8px;background-image:url(../img/modified-icon.efb2697d.svg);background-repeat:no-repeat;bottom:0;content:" ";margin:auto;margin-right:8px;position:absolute;top:0;width:1.1764705882rem;height:1.1764705882rem;margin-top:.6176470588rem;z-index:2}@media screen{[data-color-scheme=dark] .changed:after{background-image:url(../img/modified-icon.efb2697d.svg)}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .changed:after{background-image:url(../img/modified-icon.efb2697d.svg)}}.changed-added{border-color:var(--color-changes-added)}.changed-added:after{background-image:url(../img/added-icon.832a5d2c.svg)}@media screen{[data-color-scheme=dark] .changed-added:after{background-image:url(../img/added-icon.832a5d2c.svg)}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .changed-added:after{background-image:url(../img/added-icon.832a5d2c.svg)}}.changed-deprecated{border-color:var(--color-changes-deprecated)}.changed-deprecated:after{background-image:url(../img/deprecated-icon.7bf1740a.svg)}@media screen{[data-color-scheme=dark] .changed-deprecated:after{background-image:url(../img/deprecated-icon.7bf1740a.svg)}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .changed-deprecated:after{background-image:url(../img/deprecated-icon.7bf1740a.svg)}}.changed.link-block:after,.changed.relationships-item:after,.link-block .changed:after{margin-top:10px}.change-added,.change-removed{padding:2px 0}.change-removed{background-color:var(--color-highlight-red)}.change-added{background-color:var(--color-highlight-green)}body{color-scheme:light dark}body[data-color-scheme=light]{color-scheme:light}body[data-color-scheme=dark]{color-scheme:dark}body{--color-fill:#fff;--color-fill-secondary:#f7f7f7;--color-fill-tertiary:#f0f0f0;--color-fill-quaternary:#282828;--color-fill-blue:#00f;--color-fill-light-blue-secondary:#d1d1ff;--color-fill-gray:#ccc;--color-fill-gray-secondary:#f5f5f5;--color-fill-gray-tertiary:#f0f0f0;--color-fill-gray-quaternary:#f0f0f0;--color-fill-green-secondary:#f0fff0;--color-fill-orange-secondary:#fffaf6;--color-fill-red-secondary:#fff0f5;--color-figure-blue:#36f;--color-figure-gray:#000;--color-figure-gray-secondary:#666;--color-figure-gray-secondary-alt:#666;--color-figure-gray-tertiary:#666;--color-figure-green:green;--color-figure-light-gray:#666;--color-figure-orange:#c30;--color-figure-red:red;--color-tutorials-teal:#000;--color-article-background:var(--color-fill-tertiary);--color-article-body-background:var(--color-fill);--color-aside-deprecated:var(--color-figure-gray);--color-aside-deprecated-background:var(--color-fill-orange-secondary);--color-aside-deprecated-border:var(--color-figure-orange);--color-aside-experiment:var(--color-figure-gray);--color-aside-experiment-background:var(--color-fill-gray-secondary);--color-aside-experiment-border:var(--color-figure-light-gray);--color-aside-important:var(--color-figure-gray);--color-aside-important-background:var(--color-fill-gray-secondary);--color-aside-important-border:var(--color-figure-light-gray);--color-aside-note:var(--color-figure-gray);--color-aside-note-background:var(--color-fill-gray-secondary);--color-aside-note-border:var(--color-figure-light-gray);--color-aside-tip:var(--color-figure-gray);--color-aside-tip-background:var(--color-fill-gray-secondary);--color-aside-tip-border:var(--color-figure-light-gray);--color-aside-warning:var(--color-figure-gray);--color-aside-warning-background:var(--color-fill-red-secondary);--color-aside-warning-border:var(--color-figure-red);--color-badge-text:#fff;--color-badge-default:var(--color-figure-gray);--color-badge-beta:var(--color-figure-gray-tertiary);--color-badge-deprecated:var(--color-figure-orange);--color-badge-dark-default:#fff;--color-badge-dark-beta:#b0b0b0;--color-badge-dark-deprecated:#f60;--color-button-background:var(--color-fill-blue);--color-button-background-active:#36f;--color-button-background-hover:var(--color-figure-blue);--color-button-text:#fff;--color-call-to-action-background:var(--color-fill-secondary);--color-changes-added:var(--color-figure-light-gray);--color-changes-added-hover:var(--color-figure-light-gray);--color-changes-deprecated:var(--color-figure-light-gray);--color-changes-deprecated-hover:var(--color-figure-light-gray);--color-changes-modified:var(--color-figure-light-gray);--color-changes-modified-hover:var(--color-figure-light-gray);--color-changes-modified-previous-background:var(--color-fill);--color-code-background:var(--color-fill-secondary);--color-code-collapsible-background:var(--color-fill-tertiary);--color-code-collapsible-text:var(--color-figure-gray-secondary-alt);--color-code-line-highlight:rgba(51,102,255,.08);--color-code-line-highlight-border:var(--color-figure-blue);--color-code-plain:var(--color-figure-gray);--color-dropdown-background:hsla(0,0%,100%,.8);--color-dropdown-border:#ccc;--color-dropdown-option-text:#666;--color-dropdown-text:#000;--color-dropdown-dark-background:hsla(0,0%,100%,.1);--color-dropdown-dark-border:hsla(0,0%,94%,.2);--color-dropdown-dark-option-text:#ccc;--color-dropdown-dark-text:#fff;--color-eyebrow:var(--color-figure-gray-secondary);--color-focus-border-color:var(--color-fill-blue);--color-focus-color:rgba(0,125,250,.6);--color-form-error:var(--color-figure-red);--color-form-error-background:var(--color-fill-red-secondary);--color-form-valid:var(--color-figure-green);--color-form-valid-background:var(--color-fill-green-secondary);--color-generic-modal-background:var(--color-fill);--color-grid:var(--color-fill-gray);--color-header-text:var(--color-figure-gray);--color-hero-eyebrow:#ccc;--color-link:var(--color-figure-blue);--color-loading-placeholder-background:var(--color-fill);--color-nav-color:#000;--color-nav-current-link:#000;--color-nav-expanded:#fff;--color-nav-hierarchy-collapse-background:#f0f0f0;--color-nav-hierarchy-collapse-borders:#ccc;--color-nav-hierarchy-item-borders:#ccc;--color-nav-keyline:rgba(0,0,0,.2);--color-nav-link-color:#000;--color-nav-link-color-hover:#36f;--color-nav-outlines:#ccc;--color-nav-rule:hsla(0,0%,94%,.5);--color-nav-solid-background:#fff;--color-nav-sticking-expanded-keyline:rgba(0,0,0,.1);--color-nav-stuck:hsla(0,0%,100%,.9);--color-nav-uiblur-expanded:hsla(0,0%,100%,.9);--color-nav-uiblur-stuck:hsla(0,0%,100%,.7);--color-nav-root-subhead:var(--color-tutorials-teal);--color-nav-dark-border-top-color:hsla(0,0%,100%,.4);--color-nav-dark-color:#fff;--color-nav-dark-current-link:#fff;--color-nav-dark-expanded:#2a2a2a;--color-nav-dark-hierarchy-collapse-background:#424242;--color-nav-dark-hierarchy-collapse-borders:#666;--color-nav-dark-hierarchy-item-borders:#424242;--color-nav-dark-keyline:rgba(66,66,66,.95);--color-nav-dark-link-color:#fff;--color-nav-dark-link-color-hover:#09f;--color-nav-dark-outlines:#575757;--color-nav-dark-rule:#575757;--color-nav-dark-solid-background:#000;--color-nav-dark-sticking-expanded-keyline:rgba(66,66,66,.7);--color-nav-dark-stuck:rgba(42,42,42,.9);--color-nav-dark-uiblur-expanded:rgba(42,42,42,.9);--color-nav-dark-uiblur-stuck:rgba(42,42,42,.7);--color-nav-dark-root-subhead:#fff;--color-other-decl-button:var(--color-text-background);--color-runtime-preview-background:var(--color-fill-tertiary);--color-runtime-preview-disabled-text:hsla(0,0%,40%,.6);--color-runtime-preview-text:var(--color-figure-gray-secondary);--color-secondary-label:var(--color-figure-gray-secondary);--color-step-background:var(--color-fill-secondary);--color-step-caption:var(--color-figure-gray-secondary);--color-step-focused:var(--color-figure-light-gray);--color-step-text:var(--color-figure-gray-secondary);--color-svg-icon:#666;--color-syntax-addition:var(--color-figure-green);--color-syntax-attributes:#947100;--color-syntax-characters:#272ad8;--color-syntax-comments:#707f8c;--color-syntax-deletion:var(--color-figure-red);--color-syntax-documentation-markup:#506375;--color-syntax-documentation-markup-keywords:#506375;--color-syntax-heading:#ba2da2;--color-syntax-highlighted:rgba(0,113,227,.2);--color-syntax-keywords:#ad3da4;--color-syntax-marks:#000;--color-syntax-numbers:#272ad8;--color-syntax-other-class-names:#703daa;--color-syntax-other-constants:#4b21b0;--color-syntax-other-declarations:#047cb0;--color-syntax-other-function-and-method-names:#4b21b0;--color-syntax-other-instance-variables-and-globals:#703daa;--color-syntax-other-preprocessor-macros:#78492a;--color-syntax-other-type-names:#703daa;--color-syntax-param-internal-name:#404040;--color-syntax-plain-text:#000;--color-syntax-preprocessor-statements:#78492a;--color-syntax-project-class-names:#3e8087;--color-syntax-project-constants:#2d6469;--color-syntax-project-function-and-method-names:#2d6469;--color-syntax-project-instance-variables-and-globals:#3e8087;--color-syntax-project-preprocessor-macros:#78492a;--color-syntax-project-type-names:#3e8087;--color-syntax-strings:#d12f1b;--color-syntax-type-declarations:#03638c;--color-syntax-urls:#1337ff;--color-tabnav-item-border-color:var(--color-fill-gray);--color-text:var(--color-figure-gray);--color-text-background:var(--color-fill);--color-tutorial-assessments-background:var(--color-fill-secondary);--color-tutorial-background:var(--color-fill);--color-tutorial-navbar-dropdown-background:var(--color-fill);--color-tutorial-navbar-dropdown-border:var(--color-fill-gray);--color-tutorial-quiz-border-active:var(--color-figure-blue);--color-tutorials-overview-background:#161616;--color-tutorials-overview-content:#fff;--color-tutorials-overview-content-alt:#fff;--color-tutorials-overview-eyebrow:#ccc;--color-tutorials-overview-icon:#b0b0b0;--color-tutorials-overview-link:#09f;--color-tutorials-overview-navigation-link:#ccc;--color-tutorials-overview-navigation-link-active:#fff;--color-tutorials-overview-navigation-link-hover:#fff;--color-tutorial-hero-text:#fff;--color-tutorial-hero-background:#000;--color-navigator-item-hover:rgba(0,0,255,.05);--color-card-background:var(--color-fill);--color-card-content-text:var(--color-figure-gray);--color-card-eyebrow:var(--color-figure-gray-secondary-alt);--color-card-shadow:rgba(0,0,0,.04);--color-link-block-card-border:rgba(0,0,0,.04);--color-standard-red:#ffc2c2;--color-standard-orange:#fc9;--color-standard-yellow:#ffe0a3;--color-standard-blue:#9cf;--color-standard-green:#9cc;--color-standard-purple:#ccf;--color-standard-gray:#f0f0f0}@media screen{body[data-color-scheme=dark]{--color-fill:#000;--color-fill-secondary:#161616;--color-fill-tertiary:#2a2a2a;--color-fill-blue:#06f;--color-fill-light-blue-secondary:#004ec4;--color-fill-gray:#575757;--color-fill-gray-secondary:#222;--color-fill-gray-tertiary:#424242;--color-fill-gray-quaternary:#424242;--color-fill-green-secondary:#030;--color-fill-orange-secondary:#472400;--color-fill-red-secondary:#300;--color-figure-blue:#09f;--color-figure-gray:#fff;--color-figure-gray-secondary:#ccc;--color-figure-gray-secondary-alt:#b0b0b0;--color-figure-gray-tertiary:#b0b0b0;--color-figure-green:#090;--color-figure-light-gray:#b0b0b0;--color-figure-orange:#f60;--color-figure-red:#f33;--color-tutorials-teal:#fff;--color-article-body-background:#111;--color-badge-text:#000;--color-badge-default:var(--color-badge-dark-default);--color-button-background-active:#06f;--color-code-line-highlight:rgba(0,153,255,.08);--color-dropdown-background:var(--color-dropdown-dark-background);--color-dropdown-border:var(--color-dropdown-dark-border);--color-dropdown-option-text:var(--color-dropdown-dark-option-text);--color-dropdown-text:var(--color-dropdown-dark-text);--color-nav-color:var(--color-nav-dark-color);--color-nav-current-link:var(--color-nav-dark-current-link);--color-nav-expanded:var(--color-nav-dark-expanded);--color-nav-hierarchy-collapse-background:var(--color-nav-dark-hierarchy-collapse-background);--color-nav-hierarchy-collapse-borders:var(--color-nav-dark-hierarchy-collapse-borders);--color-nav-hierarchy-item-borders:var(--color-nav-dark-hierarchy-item-borders);--color-nav-keyline:var(--color-nav-dark-keyline);--color-nav-link-color:var(--color-nav-dark-link-color);--color-nav-link-color-hover:var(--color-nav-dark-link-color-hover);--color-nav-outlines:var(--color-nav-dark-outlines);--color-nav-rule:var(--color-nav-dark-rule);--color-nav-solid-background:var(--color-nav-dark-solid-background);--color-nav-sticking-expanded-keyline:var(--color-nav-dark-sticking-expanded-keyline);--color-nav-stuck:var(--color-nav-dark-stuck);--color-nav-uiblur-expanded:var(--color-nav-dark-uiblur-expanded);--color-nav-uiblur-stuck:var(--color-nav-dark-uiblur-stuck);--color-runtime-preview-disabled-text:hsla(0,0%,80%,.6);--color-syntax-attributes:#cc9768;--color-syntax-characters:#d9c97c;--color-syntax-comments:#7f8c98;--color-syntax-documentation-markup:#7f8c98;--color-syntax-documentation-markup-keywords:#a3b1bf;--color-syntax-highlighted:rgba(0,113,227,.6);--color-syntax-keywords:#ff7ab2;--color-syntax-marks:#fff;--color-syntax-numbers:#d9c97c;--color-syntax-other-class-names:#dabaff;--color-syntax-other-constants:#a7ebdd;--color-syntax-other-declarations:#4eb0cc;--color-syntax-other-function-and-method-names:#b281eb;--color-syntax-other-instance-variables-and-globals:#b281eb;--color-syntax-other-preprocessor-macros:#ffa14f;--color-syntax-other-type-names:#dabaff;--color-syntax-param-internal-name:#bfbfbf;--color-syntax-plain-text:#fff;--color-syntax-preprocessor-statements:#ffa14f;--color-syntax-project-class-names:#acf2e4;--color-syntax-project-constants:#78c2b3;--color-syntax-project-function-and-method-names:#78c2b3;--color-syntax-project-instance-variables-and-globals:#78c2b3;--color-syntax-project-preprocessor-macros:#ffa14f;--color-syntax-project-type-names:#acf2e4;--color-syntax-strings:#ff8170;--color-syntax-type-declarations:#6bdfff;--color-syntax-urls:#69f;--color-tutorial-background:var(--color-fill-tertiary);--color-navigator-item-hover:rgba(0,102,255,.5);--color-card-shadow:hsla(0,0%,100%,.04);--color-link-block-card-border:hsla(0,0%,100%,.25);--color-standard-red:#8b0000;--color-standard-orange:#8b4000;--color-standard-yellow:#8f7200;--color-standard-blue:#002d75;--color-standard-green:#023b2d;--color-standard-purple:#512b55;--color-standard-gray:#2a2a2a}}@media screen and (prefers-color-scheme:dark){body[data-color-scheme=auto]{--color-fill:#000;--color-fill-secondary:#161616;--color-fill-tertiary:#2a2a2a;--color-fill-blue:#06f;--color-fill-light-blue-secondary:#004ec4;--color-fill-gray:#575757;--color-fill-gray-secondary:#222;--color-fill-gray-tertiary:#424242;--color-fill-gray-quaternary:#424242;--color-fill-green-secondary:#030;--color-fill-orange-secondary:#472400;--color-fill-red-secondary:#300;--color-figure-blue:#09f;--color-figure-gray:#fff;--color-figure-gray-secondary:#ccc;--color-figure-gray-secondary-alt:#b0b0b0;--color-figure-gray-tertiary:#b0b0b0;--color-figure-green:#090;--color-figure-light-gray:#b0b0b0;--color-figure-orange:#f60;--color-figure-red:#f33;--color-tutorials-teal:#fff;--color-article-body-background:#111;--color-badge-text:#000;--color-badge-default:var(--color-badge-dark-default);--color-button-background-active:#06f;--color-code-line-highlight:rgba(0,153,255,.08);--color-dropdown-background:var(--color-dropdown-dark-background);--color-dropdown-border:var(--color-dropdown-dark-border);--color-dropdown-option-text:var(--color-dropdown-dark-option-text);--color-dropdown-text:var(--color-dropdown-dark-text);--color-nav-color:var(--color-nav-dark-color);--color-nav-current-link:var(--color-nav-dark-current-link);--color-nav-expanded:var(--color-nav-dark-expanded);--color-nav-hierarchy-collapse-background:var(--color-nav-dark-hierarchy-collapse-background);--color-nav-hierarchy-collapse-borders:var(--color-nav-dark-hierarchy-collapse-borders);--color-nav-hierarchy-item-borders:var(--color-nav-dark-hierarchy-item-borders);--color-nav-keyline:var(--color-nav-dark-keyline);--color-nav-link-color:var(--color-nav-dark-link-color);--color-nav-link-color-hover:var(--color-nav-dark-link-color-hover);--color-nav-outlines:var(--color-nav-dark-outlines);--color-nav-rule:var(--color-nav-dark-rule);--color-nav-solid-background:var(--color-nav-dark-solid-background);--color-nav-sticking-expanded-keyline:var(--color-nav-dark-sticking-expanded-keyline);--color-nav-stuck:var(--color-nav-dark-stuck);--color-nav-uiblur-expanded:var(--color-nav-dark-uiblur-expanded);--color-nav-uiblur-stuck:var(--color-nav-dark-uiblur-stuck);--color-runtime-preview-disabled-text:hsla(0,0%,80%,.6);--color-syntax-attributes:#cc9768;--color-syntax-characters:#d9c97c;--color-syntax-comments:#7f8c98;--color-syntax-documentation-markup:#7f8c98;--color-syntax-documentation-markup-keywords:#a3b1bf;--color-syntax-highlighted:rgba(0,113,227,.6);--color-syntax-keywords:#ff7ab2;--color-syntax-marks:#fff;--color-syntax-numbers:#d9c97c;--color-syntax-other-class-names:#dabaff;--color-syntax-other-constants:#a7ebdd;--color-syntax-other-declarations:#4eb0cc;--color-syntax-other-function-and-method-names:#b281eb;--color-syntax-other-instance-variables-and-globals:#b281eb;--color-syntax-other-preprocessor-macros:#ffa14f;--color-syntax-other-type-names:#dabaff;--color-syntax-param-internal-name:#bfbfbf;--color-syntax-plain-text:#fff;--color-syntax-preprocessor-statements:#ffa14f;--color-syntax-project-class-names:#acf2e4;--color-syntax-project-constants:#78c2b3;--color-syntax-project-function-and-method-names:#78c2b3;--color-syntax-project-instance-variables-and-globals:#78c2b3;--color-syntax-project-preprocessor-macros:#ffa14f;--color-syntax-project-type-names:#acf2e4;--color-syntax-strings:#ff8170;--color-syntax-type-declarations:#6bdfff;--color-syntax-urls:#69f;--color-tutorial-background:var(--color-fill-tertiary);--color-navigator-item-hover:rgba(0,102,255,.5);--color-card-shadow:hsla(0,0%,100%,.04);--color-link-block-card-border:hsla(0,0%,100%,.25);--color-standard-red:#8b0000;--color-standard-orange:#8b4000;--color-standard-yellow:#8f7200;--color-standard-blue:#002d75;--color-standard-green:#023b2d;--color-standard-purple:#512b55;--color-standard-gray:#2a2a2a}}#main{outline-style:none}:root{--app-height:100vh}[data-v-7420dfd2] :focus:not(input):not(textarea):not(select){outline:none}.fromkeyboard[data-v-7420dfd2] :focus:not(input):not(textarea):not(select){outline:4px solid var(--color-focus-color);outline-offset:1px}#app[data-v-7420dfd2]{display:flex;flex-flow:column;min-height:100%}#app[data-v-7420dfd2]>*{min-width:0}#app .router-content[data-v-7420dfd2]{flex:1}.container[data-v-1f05d9ec]{margin-left:auto;margin-right:auto;width:1536px;width:980px;outline-style:none;margin-top:92px;margin-bottom:140px}@media only screen and (max-width:1250px){.container[data-v-1f05d9ec]{width:692px}}@media only screen and (max-width:735px){.container[data-v-1f05d9ec]{width:87.5%}}@media only screen and (max-width:320px){.container[data-v-1f05d9ec]{width:215px}}.error-content[data-v-1f05d9ec]{box-sizing:border-box;width:502px;margin-left:auto;margin-right:auto;margin-bottom:54px}@media only screen and (max-width:1250px){.error-content[data-v-1f05d9ec]{width:420px;margin-bottom:45px}}@media only screen and (max-width:735px){.error-content[data-v-1f05d9ec]{max-width:330px;width:auto;margin-bottom:35px}}.title[data-v-1f05d9ec]{text-align:center;font-size:2.8235294118rem;line-height:1.0833333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:1250px){.title[data-v-1f05d9ec]{font-size:2.3529411765rem;line-height:1.1;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){.title[data-v-1f05d9ec]{font-size:1.8823529412rem;line-height:1.125;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}} \ No newline at end of file diff --git a/docs/css/topic.7d90b3b4.css b/docs/css/topic.7d90b3b4.css new file mode 100644 index 0000000..cea78c8 --- /dev/null +++ b/docs/css/topic.7d90b3b4.css @@ -0,0 +1,9 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */.nav-title-content[data-v-854b4dd6]{max-width:100%}.title[data-v-854b4dd6]{color:var(--color-nav-root-title,currentColor);text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:inline-block;vertical-align:top;max-width:296px}@media only screen and (max-width:1023px){.title[data-v-854b4dd6]{max-width:205px}}@media only screen and (max-width:767px){.title[data-v-854b4dd6]{flex-basis:fill;display:initial;vertical-align:initial;max-width:none}}.subhead[data-v-854b4dd6]{color:var(--color-nav-root-subhead)}.theme-dark .subhead[data-v-854b4dd6]{color:var(--color-nav-dark-root-subhead)}.mobile-dropdown[data-v-2c27d339]{box-sizing:border-box}.nav--in-breakpoint-range .mobile-dropdown[data-v-2c27d339]{padding-left:.2352941176rem;padding-right:.2352941176rem}.mobile-dropdown ul[data-v-2c27d339]{list-style:none}.mobile-dropdown .option[data-v-2c27d339]{cursor:pointer;font-size:.7058823529rem;padding:.5rem 0;display:block;text-decoration:none;color:inherit}.mobile-dropdown .option[data-v-2c27d339]:focus{outline-offset:0}.mobile-dropdown .option.depth1[data-v-2c27d339]{padding-left:.4705882353rem}.active[data-v-2c27d339],.tutorial.router-link-active[data-v-2c27d339]{font-weight:600}.active[data-v-2c27d339]:focus,.tutorial.router-link-active[data-v-2c27d339]:focus{outline:none}.chapter-list[data-v-2c27d339]:not(:first-child){margin-top:1rem}.chapter-name[data-v-2c27d339],.tutorial[data-v-2c27d339]{padding:.5rem 0;font-size:1rem;line-height:1.4705882353;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.section-list[data-v-2c27d339],.tutorial-list[data-v-2c27d339]{padding:0 .5882352941rem}.chapter-list:last-child .tutorial-list[data-v-2c27d339]:last-child{padding-bottom:10em}.chapter-list[data-v-2c27d339]{display:inline-block}.form-element[data-v-f934959a]{position:relative}.form-dropdown[data-v-f934959a]{font-size:1rem;line-height:1.2352941176;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);display:block;box-sizing:border-box;width:100%;height:3.3em;color:var(--color-dropdown-text);padding:1.1176470588rem 2.3529411765rem 0 .9411764706rem;text-align:left;border:1px solid var(--color-dropdown-border);border-radius:var(--border-radius,4px);background-clip:padding-box;margin-bottom:.8235294118rem;-webkit-appearance:none;-moz-appearance:none;appearance:none;text-overflow:ellipsis;overflow:hidden;white-space:nowrap;min-height:32px}.form-dropdown[data-v-f934959a]:focus{box-shadow:0 0 0 4px var(--color-focus-color);outline:none;border-color:var(--color-focus-border-color)}.form-dropdown.no-eyebrow[data-v-f934959a]{padding-top:0}.form-dropdown[data-v-f934959a]:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--color-dropdown-text)}.form-dropdown[data-v-f934959a]::-ms-expand{opacity:0}.form-dropdown~.form-icon[data-v-f934959a]{position:absolute;display:block;pointer-events:none;fill:var(--color-figure-gray-tertiary);right:14px;width:13px;height:auto;top:50%;transform:translateY(-50%)}.is-open .form-dropdown~.form-icon[data-v-f934959a]{transform:translateY(-50%) scale(-1)}@media only screen and (max-width:735px){.form-dropdown~.form-icon[data-v-f934959a]{right:14px}}.form-dropdown~.form-label[data-v-f934959a]{font-size:.7058823529rem;line-height:1.75;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);position:absolute;top:.4705882353rem;left:17px;color:var(--color-figure-gray-secondary);pointer-events:none;padding:0;z-index:1}.form-dropdown[data-v-f934959a] option{color:var(--color-dropdown-text)}.form-dropdown-selectnone[data-v-f934959a]{color:transparent}.form-dropdown-selectnone~.form-label[data-v-f934959a]{font-size:1rem;line-height:1.2352941176;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);top:19px;left:17px;color:var(--color-figure-gray-tertiary)}.form-dropdown-selectnone[data-v-f934959a]:-moz-focusring{text-shadow:none}.form-dropdown-selectnone[data-v-f934959a]::-ms-value{display:none}.theme-dark .form-dropdown[data-v-f934959a]{color:var(--color-dropdown-dark-text);background-color:var(--color-dropdown-dark-background);border-color:var(--color-dropdown-dark-border)}.theme-dark .form-dropdown~.form-label[data-v-f934959a]{color:#ccc}.theme-dark .form-dropdown[data-v-f934959a]:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--color-dropdown-dark-text)}.theme-dark .form-dropdown.form-dropdown-selectnone[data-v-f934959a]{color:transparent}.theme-dark .form-dropdown.form-dropdown-selectnone[data-v-f934959a]:-moz-focusring{text-shadow:none}.theme-dark .form-dropdown-selectnone~.form-label[data-v-f934959a]{color:#b0b0b0}.dropdown-small[data-v-6adda760]{height:30px;display:flex;align-items:center;position:relative;background:var(--color-fill)}.dropdown-small .form-dropdown-toggle[data-v-6adda760]{line-height:1.5;font-size:12px;padding-top:0;padding-bottom:0;padding-left:20px;min-height:unset;height:30px;display:flex;align-items:center}.dropdown-small .form-dropdown-toggle[data-v-6adda760]:focus{box-shadow:none;border-color:var(--color-dropdown-border)}.fromkeyboard .dropdown-small .form-dropdown-toggle[data-v-6adda760]:focus{box-shadow:0 0 0 2px var(--color-focus-color);outline:none;border-color:var(--color-focus-border-color)}.form-dropdown-toggle[data-v-6adda760]{margin:0}.is-open .form-dropdown-toggle[data-v-6adda760]{border-radius:var(--border-radius,4px) var(--border-radius,4px) 0 0;border-bottom:none;padding-bottom:1px}.fromkeyboard .is-open .form-dropdown-toggle[data-v-6adda760]{box-shadow:1px -1px 0 1px var(--color-focus-color),-1px -1px 0 1px var(--color-focus-color);border-color:var(--color-focus-border-color)}.form-dropdown-title[data-v-6adda760]{margin:0;padding:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.dropdown-custom[data-v-6adda760]{border-radius:var(--border-radius,4px)}.dropdown-custom.is-open[data-v-6adda760]{border-radius:var(--border-radius,4px) var(--border-radius,4px) 0 0}.dropdown-custom[data-v-6adda760] .form-dropdown-content{background:var(--color-fill);position:absolute;right:0;left:0;top:100%;border-bottom-left-radius:var(--border-radius,4px);border-bottom-right-radius:var(--border-radius,4px);border:1px solid var(--color-dropdown-border);border-top:none;display:none;overflow-y:auto}.dropdown-custom[data-v-6adda760] .form-dropdown-content.is-open{display:block}.fromkeyboard .dropdown-custom[data-v-6adda760] .form-dropdown-content.is-open{box-shadow:1px 1px 0 1px var(--color-focus-color),-1px 1px 0 1px var(--color-focus-color);border-color:var(--color-focus-border-color);border-top-color:transparent}.nav .dropdown-custom[data-v-6adda760] .form-dropdown-content{max-height:calc(100vh - 116px - 3.05882rem)}.nav--is-sticking.nav .dropdown-custom[data-v-6adda760] .form-dropdown-content{max-height:calc(100vh - 3.05882rem - 72px)}.dropdown-custom[data-v-6adda760] .options{list-style:none;margin:0;padding:0 0 20px}.dropdown-custom[data-v-6adda760] .option{cursor:pointer;padding:5px 20px;font-size:12px;line-height:20px;outline:none}.dropdown-custom[data-v-6adda760] .option:hover{background-color:var(--color-fill-tertiary)}.dropdown-custom[data-v-6adda760] .option.option-active{font-weight:600}.fromkeyboard .dropdown-custom[data-v-6adda760] .option:hover{background-color:transparent}.fromkeyboard .dropdown-custom[data-v-6adda760] .option:focus{background-color:var(--color-fill-tertiary);outline:none}.tutorial-dropdown[data-v-618ff780]{grid-column:3}.section-tracker[data-v-618ff780]{font-size:.7058823529rem;line-height:1.3333333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-figure-gray-secondary);margin-left:15px}.tutorial-dropdown[data-v-03cbd7f7]{grid-column:1/2}.tutorial-dropdown .options[data-v-03cbd7f7]{padding-top:1rem;padding-bottom:0}.tutorial-dropdown .option[data-v-03cbd7f7]{padding:5px 20px 5px 30px}.chapter-list[data-v-03cbd7f7]{padding-bottom:20px}.chapter-name[data-v-03cbd7f7]{margin:0 20px 5px 20px;line-height:normal;color:var(--color-figure-gray-secondary)}.chevron-icon[data-v-1d3fe8ed]{padding:0;color:var(--color-nav-outlines);grid-column:2;height:20px;width:20px;margin:0 4px}@media only screen and (min-width:768px){.nav[data-v-1d3fe8ed] .nav-content{display:grid;grid-template-columns:auto auto 3fr;align-items:center}.nav[data-v-1d3fe8ed] .nav-menu{padding:0;justify-content:flex-start;grid-column:3/5}.nav[data-v-1d3fe8ed] .nav-menu-item{margin:0}}.dropdown-container[data-v-1d3fe8ed]{height:3.0588235294rem;display:grid;grid-template-columns:minmax(230px,285px) auto minmax(230px,1fr);align-items:center}@media only screen and (max-width:1023px){.dropdown-container[data-v-1d3fe8ed]{grid-template-columns:minmax(173px,216px) auto minmax(173px,1fr)}}@media(scripting:none){.dropdown-container[data-v-1d3fe8ed]{display:none}}.separator[data-v-1d3fe8ed]{height:20px;border-right:1px solid;border-color:var(--color-nav-outlines);margin:0 20px;grid-column:2}@media(scripting:none){.separator[data-v-1d3fe8ed]{display:none}}.mobile-dropdown-container[data-v-1d3fe8ed],.nav--in-breakpoint-range.nav .dropdown-container[data-v-1d3fe8ed],.nav--in-breakpoint-range.nav .separator[data-v-1d3fe8ed]{display:none}.nav--in-breakpoint-range.nav .mobile-dropdown-container[data-v-1d3fe8ed]{display:block}.nav--in-breakpoint-range.nav[data-v-1d3fe8ed] .nav-title{grid-area:title}.nav--in-breakpoint-range.nav[data-v-1d3fe8ed] .pre-title{display:none}.nav[data-v-1d3fe8ed] .nav-title{grid-column:1;width:90%;padding-top:0}.primary-dropdown[data-v-1d3fe8ed],.secondary-dropdown[data-v-1d3fe8ed]{background:var(--color-tutorial-navbar-dropdown-background);border-color:var(--color-tutorial-navbar-dropdown-border)}.primary-dropdown[data-v-1d3fe8ed] .form-dropdown,.primary-dropdown[data-v-1d3fe8ed] .form-dropdown:focus,.secondary-dropdown[data-v-1d3fe8ed] .form-dropdown,.secondary-dropdown[data-v-1d3fe8ed] .form-dropdown:focus{border-color:var(--color-tutorial-navbar-dropdown-border)}.primary-dropdown[data-v-1d3fe8ed] .options,.secondary-dropdown[data-v-1d3fe8ed] .options{background:var(--color-tutorial-navbar-dropdown-background);border-color:var(--color-tutorial-navbar-dropdown-border)}[data-v-0861b5be] .code-listing+*,[data-v-0861b5be] aside+*,[data-v-0861b5be] h2+*,[data-v-0861b5be] h3+*,[data-v-0861b5be] ol+*,[data-v-0861b5be] p+*,[data-v-0861b5be] ul+*{margin-top:20px}[data-v-0861b5be] ol ol,[data-v-0861b5be] ol ul,[data-v-0861b5be] ul ol,[data-v-0861b5be] ul ul{margin-top:0}[data-v-0861b5be] h2{font-size:1.8823529412rem;line-height:1.25;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:1250px){[data-v-0861b5be] h2{font-size:1.6470588235rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){[data-v-0861b5be] h2{font-size:1.4117647059rem;line-height:1.3333333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}[data-v-0861b5be] h3{font-size:1.4117647059rem;line-height:1.1666666667;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:1250px){[data-v-0861b5be] h3{font-size:1.2352941176rem;line-height:1.1904761905;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}[data-v-0861b5be] .code-listing{background:var(--color-code-background);border-color:var(--colors-grid,var(--color-grid));border-style:solid;border-width:1px}[data-v-0861b5be] .code-listing pre{font-size:.7058823529rem;line-height:1.8333333333;font-weight:400;font-family:var(--typography-html-font-mono,Menlo,monospace);padding:20px 0}.columns[data-v-30edf911]{display:grid;grid-template-rows:repeat(2,auto)}.columns.cols-2[data-v-30edf911]{gap:20px 8.3333333333%;grid-template-columns:repeat(2,1fr)}.columns.cols-3[data-v-30edf911]{gap:20px 4.1666666667%;grid-template-columns:repeat(3,1fr)}.asset[data-v-30edf911]{align-self:end;grid-row:1}.content[data-v-30edf911]{grid-row:2}@media only screen and (max-width:735px){.columns.cols-2[data-v-30edf911],.columns.cols-3[data-v-30edf911]{grid-template-columns:unset}.asset[data-v-30edf911],.content[data-v-30edf911]{grid-row:auto}}.content-and-media[data-v-3fa44f9e]{display:flex}.content-and-media.media-leading[data-v-3fa44f9e]{flex-direction:row-reverse}.content-and-media.media-trailing[data-v-3fa44f9e]{flex-direction:row}@media only screen and (min-width:736px){.content-and-media[data-v-3fa44f9e]{align-items:center;justify-content:center}}.content[data-v-3fa44f9e]{width:62.5%}.asset[data-v-3fa44f9e]{width:29.1666666667%}.media-leading .asset[data-v-3fa44f9e]{margin-right:8.3333333333%}.media-trailing .asset[data-v-3fa44f9e]{margin-left:8.3333333333%}@media only screen and (max-width:735px){.content-and-media.media-leading[data-v-3fa44f9e],.content-and-media.media-trailing[data-v-3fa44f9e]{flex-direction:column}.asset[data-v-3fa44f9e],.content[data-v-3fa44f9e]{width:100%}.media-leading .asset[data-v-3fa44f9e],.media-trailing .asset[data-v-3fa44f9e]{margin:20px 0 0 0}}.group[id][data-v-5b4a8b3c]{margin-top:20px;padding-top:20px}[data-v-5b4a8b3c] img,[data-v-5b4a8b3c] video{display:block;margin:0 auto;max-width:100%}.layout+[data-v-4d5a806e]{margin-top:40px}@media only screen and (max-width:735px){.layout[data-v-4d5a806e]:first-child>:not(.group[id]){margin-top:40px}}.body[data-v-20dca692]{background:var(--colors-text-background,var(--color-article-body-background));margin-left:auto;margin-right:auto;width:1536px;width:980px;border-radius:10px;transform:translateY(-120px)}@media only screen and (max-width:1250px){.body[data-v-20dca692]{width:692px}}@media only screen and (max-width:735px){.body[data-v-20dca692]{width:87.5%}}@media only screen and (max-width:320px){.body[data-v-20dca692]{width:215px}}@media only screen and (max-width:735px){.body[data-v-20dca692]{border-radius:0;transform:none}}.body[data-v-20dca692]~*{margin-top:-40px}.body-content[data-v-20dca692]{padding:40px 8.3333333333% 80px 8.3333333333%}@media only screen and (max-width:735px){.body-content[data-v-20dca692]{padding:0 0 40px 0}}.call-to-action[data-v-2bfdf182]{padding:65px 0;background:var(--color-call-to-action-background)}.theme-dark .call-to-action[data-v-2bfdf182]{--color-call-to-action-background:#424242}.row[data-v-2bfdf182]{margin-left:auto;margin-right:auto;width:1536px;width:980px;display:flex;align-items:center}@media only screen and (max-width:1250px){.row[data-v-2bfdf182]{width:692px}}@media only screen and (max-width:735px){.row[data-v-2bfdf182]{width:87.5%}}@media only screen and (max-width:320px){.row[data-v-2bfdf182]{width:215px}}[data-v-2bfdf182] img,[data-v-2bfdf182] video{max-height:560px}h2[data-v-2bfdf182]{font-size:1.8823529412rem;line-height:1.25;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:1250px){h2[data-v-2bfdf182]{font-size:1.6470588235rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){h2[data-v-2bfdf182]{font-size:1.4117647059rem;line-height:1.3333333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.label[data-v-2bfdf182]{display:block;font-size:1.2352941176rem;line-height:1.1904761905;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);margin-bottom:var(--spacing-stacked-margin-small);color:var(--color-eyebrow)}@media only screen and (max-width:735px){.label[data-v-2bfdf182]{font-size:1.1176470588rem;line-height:1.2105263158;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.content[data-v-2bfdf182]{margin-bottom:1.5rem}.right-column[data-v-2bfdf182]{margin-left:auto}@media only screen and (max-width:735px){.row[data-v-2bfdf182]{display:block}.col+.col[data-v-2bfdf182]{margin-top:40px}.call-to-action[data-v-426a965c]{margin-top:0}}.headline[data-v-d46a1474]{margin-bottom:var(--spacing-stacked-margin-large)}.heading[data-v-d46a1474]{font-size:2.8235294118rem;line-height:1.0833333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-header-text)}@media only screen and (max-width:1250px){.heading[data-v-d46a1474]{font-size:2.3529411765rem;line-height:1.1;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){.heading[data-v-d46a1474]{font-size:1.8823529412rem;line-height:1.125;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.dark .heading[data-v-d46a1474]{color:#fff}.eyebrow[data-v-d46a1474]{font-size:1.4117647059rem;line-height:1.1666666667;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);display:block;margin-bottom:var(--spacing-stacked-margin-small);color:var(--color-eyebrow)}@media only screen and (max-width:1250px){.eyebrow[data-v-d46a1474]{font-size:1.2352941176rem;line-height:1.1904761905;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.generic-modal[data-v-795f7b59]{position:fixed;top:0;left:0;right:0;bottom:0;margin:0;z-index:11000;display:flex;align-items:center;justify-content:center;flex-wrap:wrap;background:none;overflow:auto}.modal-fullscreen[data-v-795f7b59]{align-items:stretch}.modal-fullscreen .container[data-v-795f7b59]{margin:0;flex:1;width:100%;height:100%;padding-top:env(safe-area-inset-top);padding-right:env(safe-area-inset-right);padding-bottom:env(safe-area-inset-bottom);padding-left:env(safe-area-inset-left)}.modal-standard[data-v-795f7b59]{padding:20px}.modal-standard .container[data-v-795f7b59]{padding:60px;border-radius:var(--border-radius,4px)}@media screen{[data-color-scheme=dark] .modal-standard .container[data-v-795f7b59]{background:#1d1d1f}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .modal-standard .container[data-v-795f7b59]{background:#1d1d1f}}@media only screen and (max-width:735px){.modal-standard[data-v-795f7b59]{padding:0;align-items:stretch}.modal-standard .container[data-v-795f7b59]{margin:20px 0 0;padding:50px 30px;flex:1;width:100%;border-bottom-left-radius:0;border-bottom-right-radius:0}}.backdrop[data-v-795f7b59]{overflow:auto;background:var(--backdrop-background,rgba(0,0,0,.4));-webkit-overflow-scrolling:touch;width:100%;height:100%;position:fixed}.container[data-v-795f7b59]{margin-left:auto;margin-right:auto;width:1536px;width:980px;background:var(--colors-generic-modal-background,var(--color-generic-modal-background));z-index:1;position:relative;overflow:auto;max-width:100%}@media only screen and (max-width:1250px){.container[data-v-795f7b59]{width:692px}}@media only screen and (max-width:735px){.container[data-v-795f7b59]{width:87.5%}}@media only screen and (max-width:320px){.container[data-v-795f7b59]{width:215px}}.close[data-v-795f7b59]{position:absolute;z-index:9999;top:22px;left:22px;width:17px;height:17px;color:#666;cursor:pointer;background:none;border:0;display:flex;align-items:center}.close .close-icon[data-v-795f7b59]{fill:currentColor;width:100%;height:100%}.theme-dark .container[data-v-795f7b59]{background:#000}.theme-dark .container .close[data-v-795f7b59]{color:#b0b0b0}.theme-code .container[data-v-795f7b59]{background-color:var(--code-background,var(--color-code-background))}.metadata[data-v-94ff76c0]{display:flex}.item[data-v-94ff76c0]{font-size:.7058823529rem;line-height:1.3333333333;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);display:flex;flex-direction:column;justify-content:flex-end;align-items:center;border-right:1px solid #fff;padding:0 27.5px}@media only screen and (max-width:735px){.item[data-v-94ff76c0]{font-size:.6470588235rem;line-height:1.6363636364;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);padding:0 8px}}.item[data-v-94ff76c0]:first-of-type{padding-left:0}.item[data-v-94ff76c0]:last-of-type{border:none}@media only screen and (max-width:735px){.item[data-v-94ff76c0]:last-of-type{padding-right:0}}.content[data-v-94ff76c0]{color:#fff}.icon[data-v-94ff76c0]{font-size:2.8235294118rem;line-height:1.0833333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:1250px){.icon[data-v-94ff76c0]{font-size:2.3529411765rem;line-height:1.1;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){.icon[data-v-94ff76c0]{font-size:1.8823529412rem;line-height:1.125;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.small-icon[data-v-94ff76c0]{width:1em;height:1em;margin-left:.2rem}.small-icon.xcode-icon[data-v-94ff76c0]{width:.8em;height:.8em}.content-link[data-v-94ff76c0]{display:flex;align-items:center}a[data-v-94ff76c0]{color:var(--colors-link,var(--color-tutorials-overview-link))}.duration[data-v-94ff76c0]{display:flex;align-items:baseline;font-size:2.3529411765rem;line-height:1;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);line-height:1.8rem}@media only screen and (max-width:735px){.duration[data-v-94ff76c0]{font-size:1.6470588235rem;line-height:1;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);line-height:1.3rem}}.minutes[data-v-94ff76c0]{display:inline-block;font-size:1.6470588235rem;line-height:1;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);line-height:1.3rem}@media only screen and (max-width:735px){.minutes[data-v-94ff76c0]{font-size:1rem;line-height:1.2352941176;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);line-height:.8rem}}.item-large-icon[data-v-94ff76c0]{height:2.3rem;max-width:100%}@media only screen and (max-width:735px){.item-large-icon[data-v-94ff76c0]{height:1.5rem;max-width:100%}}.bottom[data-v-94ff76c0]{margin-top:13px}@media only screen and (max-width:735px){.bottom[data-v-94ff76c0]{margin-top:8px}}.hero[data-v-2a434750]{background-color:var(--color-tutorial-hero-background);color:var(--color-tutorial-hero-text);position:relative}@media screen{.hero.dark[data-v-2a434750]{--color-fill:#fff;--color-fill-secondary:#f7f7f7;--color-fill-tertiary:#f0f0f0;--color-fill-quaternary:#282828;--color-fill-blue:#00f;--color-fill-light-blue-secondary:#d1d1ff;--color-fill-gray:#ccc;--color-fill-gray-secondary:#f5f5f5;--color-fill-gray-tertiary:#f0f0f0;--color-fill-gray-quaternary:#f0f0f0;--color-fill-green-secondary:#f0fff0;--color-fill-orange-secondary:#fffaf6;--color-fill-red-secondary:#fff0f5;--color-figure-blue:#36f;--color-figure-gray:#000;--color-figure-gray-secondary:#666;--color-figure-gray-secondary-alt:#666;--color-figure-gray-tertiary:#666;--color-figure-green:green;--color-figure-light-gray:#666;--color-figure-orange:#c30;--color-figure-red:red;--color-tutorials-teal:#000;--color-article-background:var(--color-fill-tertiary);--color-article-body-background:var(--color-fill);--color-aside-deprecated:var(--color-figure-gray);--color-aside-deprecated-background:var(--color-fill-orange-secondary);--color-aside-deprecated-border:var(--color-figure-orange);--color-aside-experiment:var(--color-figure-gray);--color-aside-experiment-background:var(--color-fill-gray-secondary);--color-aside-experiment-border:var(--color-figure-light-gray);--color-aside-important:var(--color-figure-gray);--color-aside-important-background:var(--color-fill-gray-secondary);--color-aside-important-border:var(--color-figure-light-gray);--color-aside-note:var(--color-figure-gray);--color-aside-note-background:var(--color-fill-gray-secondary);--color-aside-note-border:var(--color-figure-light-gray);--color-aside-tip:var(--color-figure-gray);--color-aside-tip-background:var(--color-fill-gray-secondary);--color-aside-tip-border:var(--color-figure-light-gray);--color-aside-warning:var(--color-figure-gray);--color-aside-warning-background:var(--color-fill-red-secondary);--color-aside-warning-border:var(--color-figure-red);--color-badge-text:#fff;--color-badge-default:var(--color-figure-gray);--color-badge-beta:var(--color-figure-gray-tertiary);--color-badge-deprecated:var(--color-figure-orange);--color-badge-dark-default:#fff;--color-badge-dark-beta:#b0b0b0;--color-badge-dark-deprecated:#f60;--color-button-background:var(--color-fill-blue);--color-button-background-active:#36f;--color-button-background-hover:var(--color-figure-blue);--color-button-text:#fff;--color-call-to-action-background:var(--color-fill-secondary);--color-changes-added:var(--color-figure-light-gray);--color-changes-added-hover:var(--color-figure-light-gray);--color-changes-deprecated:var(--color-figure-light-gray);--color-changes-deprecated-hover:var(--color-figure-light-gray);--color-changes-modified:var(--color-figure-light-gray);--color-changes-modified-hover:var(--color-figure-light-gray);--color-changes-modified-previous-background:var(--color-fill);--color-code-background:var(--color-fill-secondary);--color-code-collapsible-background:var(--color-fill-tertiary);--color-code-collapsible-text:var(--color-figure-gray-secondary-alt);--color-code-line-highlight:rgba(51,102,255,.08);--color-code-line-highlight-border:var(--color-figure-blue);--color-code-plain:var(--color-figure-gray);--color-dropdown-background:hsla(0,0%,100%,.8);--color-dropdown-border:#ccc;--color-dropdown-option-text:#666;--color-dropdown-text:#000;--color-dropdown-dark-background:hsla(0,0%,100%,.1);--color-dropdown-dark-border:hsla(0,0%,94%,.2);--color-dropdown-dark-option-text:#ccc;--color-dropdown-dark-text:#fff;--color-eyebrow:var(--color-figure-gray-secondary);--color-focus-border-color:var(--color-fill-blue);--color-focus-color:rgba(0,125,250,.6);--color-form-error:var(--color-figure-red);--color-form-error-background:var(--color-fill-red-secondary);--color-form-valid:var(--color-figure-green);--color-form-valid-background:var(--color-fill-green-secondary);--color-generic-modal-background:var(--color-fill);--color-grid:var(--color-fill-gray);--color-header-text:var(--color-figure-gray);--color-hero-eyebrow:#ccc;--color-link:var(--color-figure-blue);--color-loading-placeholder-background:var(--color-fill);--color-nav-color:#000;--color-nav-current-link:#000;--color-nav-expanded:#fff;--color-nav-hierarchy-collapse-background:#f0f0f0;--color-nav-hierarchy-collapse-borders:#ccc;--color-nav-hierarchy-item-borders:#ccc;--color-nav-keyline:rgba(0,0,0,.2);--color-nav-link-color:#000;--color-nav-link-color-hover:#36f;--color-nav-outlines:#ccc;--color-nav-rule:hsla(0,0%,94%,.5);--color-nav-solid-background:#fff;--color-nav-sticking-expanded-keyline:rgba(0,0,0,.1);--color-nav-stuck:hsla(0,0%,100%,.9);--color-nav-uiblur-expanded:hsla(0,0%,100%,.9);--color-nav-uiblur-stuck:hsla(0,0%,100%,.7);--color-nav-root-subhead:var(--color-tutorials-teal);--color-nav-dark-border-top-color:hsla(0,0%,100%,.4);--color-nav-dark-color:#fff;--color-nav-dark-current-link:#fff;--color-nav-dark-expanded:#2a2a2a;--color-nav-dark-hierarchy-collapse-background:#424242;--color-nav-dark-hierarchy-collapse-borders:#666;--color-nav-dark-hierarchy-item-borders:#424242;--color-nav-dark-keyline:rgba(66,66,66,.95);--color-nav-dark-link-color:#fff;--color-nav-dark-link-color-hover:#09f;--color-nav-dark-outlines:#575757;--color-nav-dark-rule:#575757;--color-nav-dark-solid-background:#000;--color-nav-dark-sticking-expanded-keyline:rgba(66,66,66,.7);--color-nav-dark-stuck:rgba(42,42,42,.9);--color-nav-dark-uiblur-expanded:rgba(42,42,42,.9);--color-nav-dark-uiblur-stuck:rgba(42,42,42,.7);--color-nav-dark-root-subhead:#fff;--color-other-decl-button:var(--color-text-background);--color-runtime-preview-background:var(--color-fill-tertiary);--color-runtime-preview-disabled-text:hsla(0,0%,40%,.6);--color-runtime-preview-text:var(--color-figure-gray-secondary);--color-secondary-label:var(--color-figure-gray-secondary);--color-step-background:var(--color-fill-secondary);--color-step-caption:var(--color-figure-gray-secondary);--color-step-focused:var(--color-figure-light-gray);--color-step-text:var(--color-figure-gray-secondary);--color-svg-icon:#666;--color-syntax-addition:var(--color-figure-green);--color-syntax-attributes:#947100;--color-syntax-characters:#272ad8;--color-syntax-comments:#707f8c;--color-syntax-deletion:var(--color-figure-red);--color-syntax-documentation-markup:#506375;--color-syntax-documentation-markup-keywords:#506375;--color-syntax-heading:#ba2da2;--color-syntax-highlighted:rgba(0,113,227,.2);--color-syntax-keywords:#ad3da4;--color-syntax-marks:#000;--color-syntax-numbers:#272ad8;--color-syntax-other-class-names:#703daa;--color-syntax-other-constants:#4b21b0;--color-syntax-other-declarations:#047cb0;--color-syntax-other-function-and-method-names:#4b21b0;--color-syntax-other-instance-variables-and-globals:#703daa;--color-syntax-other-preprocessor-macros:#78492a;--color-syntax-other-type-names:#703daa;--color-syntax-param-internal-name:#404040;--color-syntax-plain-text:#000;--color-syntax-preprocessor-statements:#78492a;--color-syntax-project-class-names:#3e8087;--color-syntax-project-constants:#2d6469;--color-syntax-project-function-and-method-names:#2d6469;--color-syntax-project-instance-variables-and-globals:#3e8087;--color-syntax-project-preprocessor-macros:#78492a;--color-syntax-project-type-names:#3e8087;--color-syntax-strings:#d12f1b;--color-syntax-type-declarations:#03638c;--color-syntax-urls:#1337ff;--color-tabnav-item-border-color:var(--color-fill-gray);--color-text:var(--color-figure-gray);--color-text-background:var(--color-fill);--color-tutorial-assessments-background:var(--color-fill-secondary);--color-tutorial-background:var(--color-fill);--color-tutorial-navbar-dropdown-background:var(--color-fill);--color-tutorial-navbar-dropdown-border:var(--color-fill-gray);--color-tutorial-quiz-border-active:var(--color-figure-blue);--color-tutorials-overview-background:#161616;--color-tutorials-overview-content:#fff;--color-tutorials-overview-content-alt:#fff;--color-tutorials-overview-eyebrow:#ccc;--color-tutorials-overview-icon:#b0b0b0;--color-tutorials-overview-link:#09f;--color-tutorials-overview-navigation-link:#ccc;--color-tutorials-overview-navigation-link-active:#fff;--color-tutorials-overview-navigation-link-hover:#fff;--color-tutorial-hero-text:#fff;--color-tutorial-hero-background:#000;--color-navigator-item-hover:rgba(0,0,255,.05);--color-card-background:var(--color-fill);--color-card-content-text:var(--color-figure-gray);--color-card-eyebrow:var(--color-figure-gray-secondary-alt);--color-card-shadow:rgba(0,0,0,.04);--color-link-block-card-border:rgba(0,0,0,.04);--color-standard-red:#ffc2c2;--color-standard-orange:#fc9;--color-standard-yellow:#ffe0a3;--color-standard-blue:#9cf;--color-standard-green:#9cc;--color-standard-purple:#ccf;--color-standard-gray:#f0f0f0;--color-fill:#000;--color-fill-secondary:#161616;--color-fill-tertiary:#2a2a2a;--color-fill-blue:#06f;--color-fill-light-blue-secondary:#004ec4;--color-fill-gray:#575757;--color-fill-gray-secondary:#222;--color-fill-gray-tertiary:#424242;--color-fill-gray-quaternary:#424242;--color-fill-green-secondary:#030;--color-fill-orange-secondary:#472400;--color-fill-red-secondary:#300;--color-figure-blue:#09f;--color-figure-gray:#fff;--color-figure-gray-secondary:#ccc;--color-figure-gray-secondary-alt:#b0b0b0;--color-figure-gray-tertiary:#b0b0b0;--color-figure-green:#090;--color-figure-light-gray:#b0b0b0;--color-figure-orange:#f60;--color-figure-red:#f33;--color-tutorials-teal:#fff;--color-article-body-background:#111;--color-badge-text:#000;--color-badge-default:var(--color-badge-dark-default);--color-button-background-active:#06f;--color-code-line-highlight:rgba(0,153,255,.08);--color-dropdown-background:var(--color-dropdown-dark-background);--color-dropdown-border:var(--color-dropdown-dark-border);--color-dropdown-option-text:var(--color-dropdown-dark-option-text);--color-dropdown-text:var(--color-dropdown-dark-text);--color-nav-color:var(--color-nav-dark-color);--color-nav-current-link:var(--color-nav-dark-current-link);--color-nav-expanded:var(--color-nav-dark-expanded);--color-nav-hierarchy-collapse-background:var(--color-nav-dark-hierarchy-collapse-background);--color-nav-hierarchy-collapse-borders:var(--color-nav-dark-hierarchy-collapse-borders);--color-nav-hierarchy-item-borders:var(--color-nav-dark-hierarchy-item-borders);--color-nav-keyline:var(--color-nav-dark-keyline);--color-nav-link-color:var(--color-nav-dark-link-color);--color-nav-link-color-hover:var(--color-nav-dark-link-color-hover);--color-nav-outlines:var(--color-nav-dark-outlines);--color-nav-rule:var(--color-nav-dark-rule);--color-nav-solid-background:var(--color-nav-dark-solid-background);--color-nav-sticking-expanded-keyline:var(--color-nav-dark-sticking-expanded-keyline);--color-nav-stuck:var(--color-nav-dark-stuck);--color-nav-uiblur-expanded:var(--color-nav-dark-uiblur-expanded);--color-nav-uiblur-stuck:var(--color-nav-dark-uiblur-stuck);--color-runtime-preview-disabled-text:hsla(0,0%,80%,.6);--color-syntax-attributes:#cc9768;--color-syntax-characters:#d9c97c;--color-syntax-comments:#7f8c98;--color-syntax-documentation-markup:#7f8c98;--color-syntax-documentation-markup-keywords:#a3b1bf;--color-syntax-highlighted:rgba(0,113,227,.6);--color-syntax-keywords:#ff7ab2;--color-syntax-marks:#fff;--color-syntax-numbers:#d9c97c;--color-syntax-other-class-names:#dabaff;--color-syntax-other-constants:#a7ebdd;--color-syntax-other-declarations:#4eb0cc;--color-syntax-other-function-and-method-names:#b281eb;--color-syntax-other-instance-variables-and-globals:#b281eb;--color-syntax-other-preprocessor-macros:#ffa14f;--color-syntax-other-type-names:#dabaff;--color-syntax-param-internal-name:#bfbfbf;--color-syntax-plain-text:#fff;--color-syntax-preprocessor-statements:#ffa14f;--color-syntax-project-class-names:#acf2e4;--color-syntax-project-constants:#78c2b3;--color-syntax-project-function-and-method-names:#78c2b3;--color-syntax-project-instance-variables-and-globals:#78c2b3;--color-syntax-project-preprocessor-macros:#ffa14f;--color-syntax-project-type-names:#acf2e4;--color-syntax-strings:#ff8170;--color-syntax-type-declarations:#6bdfff;--color-syntax-urls:#69f;--color-tutorial-background:var(--color-fill-tertiary);--color-navigator-item-hover:rgba(0,102,255,.5);--color-card-shadow:hsla(0,0%,100%,.04);--color-link-block-card-border:hsla(0,0%,100%,.25);--color-standard-red:#8b0000;--color-standard-orange:#8b4000;--color-standard-yellow:#8f7200;--color-standard-blue:#002d75;--color-standard-green:#023b2d;--color-standard-purple:#512b55;--color-standard-gray:#2a2a2a}}.bg[data-v-2a434750]{background-color:var(--color-tutorial-hero-background);background-position:top;background-repeat:no-repeat;background-size:cover;content:"";height:100%;left:0;opacity:.3;position:absolute;top:0;width:100%}.row[data-v-2a434750]{margin-left:auto;margin-right:auto;width:1536px;width:980px;padding:80px 0}@media only screen and (max-width:1250px){.row[data-v-2a434750]{width:692px}}@media only screen and (max-width:735px){.row[data-v-2a434750]{width:87.5%}}@media only screen and (max-width:320px){.row[data-v-2a434750]{width:215px}}.col[data-v-2a434750]{z-index:1}[data-v-2a434750] .eyebrow{font-size:1.4117647059rem;line-height:1.1666666667;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-hero-eyebrow)}@media only screen and (max-width:1250px){[data-v-2a434750] .eyebrow{font-size:1.2352941176rem;line-height:1.1904761905;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.headline[data-v-2a434750]{font-size:2.8235294118rem;line-height:1.0833333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);margin-bottom:2rem}@media only screen and (max-width:1250px){.headline[data-v-2a434750]{font-size:2.3529411765rem;line-height:1.1;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){.headline[data-v-2a434750]{font-size:1.8823529412rem;line-height:1.125;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.intro[data-v-2a434750]{font-size:1.2352941176rem;line-height:1.380952381;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:735px){.intro[data-v-2a434750]{font-size:1.1176470588rem;line-height:1.4210526316;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.content+p[data-v-2a434750]{margin-top:var(--spacing-stacked-margin-large)}@media only screen and (max-width:735px){.content+p[data-v-2a434750]{margin-top:8px}}.call-to-action[data-v-2a434750]{display:flex;align-items:center}.call-to-action .cta-icon[data-v-2a434750]{margin-left:.4rem;width:1em;height:1em}.metadata[data-v-2a434750]{margin-top:2rem}.video-asset[data-v-2a434750]{display:grid;height:100vh;margin:0;place-items:center center}.video-asset[data-v-2a434750] video{max-width:1280px;min-width:320px;width:100%}@media only screen and (max-width:735px){.headline[data-v-2a434750]{margin-bottom:19px}}.tutorial-hero[data-v-35a9482f]{margin-bottom:80px}@media only screen and (max-width:735px){.tutorial-hero[data-v-35a9482f]{margin-bottom:0}}.title[data-v-28135d78]{font-size:.7058823529rem;line-height:1.3333333333;color:var(--colors-secondary-label,var(--color-secondary-label))}.title[data-v-28135d78],.title[data-v-61b03ec2]{font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.title[data-v-61b03ec2]{font-size:1.1176470588rem;line-height:1.2105263158;color:var(--colors-header-text,var(--color-header-text));margin:25px 0}.question-content[data-v-61b03ec2] code{font-size:.7647058824rem;line-height:1.8461538462;font-weight:400;font-family:var(--typography-html-font-mono,Menlo,monospace)}.choices[data-v-61b03ec2]{display:flex;flex-direction:column;padding:0;list-style:none;margin:25px 0}.choice[data-v-61b03ec2]{font-size:.8235294118rem;line-height:1.4285714286;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);flex:1;border-radius:var(--border-radius,4px);margin:8px 0;padding:1.5rem 40px;cursor:pointer;background:var(--colors-text-background,var(--color-text-background));display:flex;flex-direction:column;justify-content:center;border-width:1px;border-style:solid;border-color:var(--colors-grid,var(--color-grid));position:relative}.choice[data-v-61b03ec2] img{max-height:23.5294117647rem}.choice[data-v-61b03ec2]:first-of-type{margin-top:0}.choice[data-v-61b03ec2] code{font-size:.7647058824rem;line-height:1.8461538462;font-weight:400;font-family:var(--typography-html-font-mono,Menlo,monospace)}.controls[data-v-61b03ec2]{text-align:center;margin-bottom:40px}.controls .button-cta[data-v-61b03ec2]{margin:.5rem;margin-top:0;padding:.3rem 3rem;min-width:8rem}input[type=radio][data-v-61b03ec2]{position:absolute;width:100%;left:0;height:100%;opacity:0;z-index:-1}.active[data-v-61b03ec2]{border-color:var(--color-tutorial-quiz-border-active);box-shadow:0 0 0 4px var(--color-focus-color);outline:none}.active [data-v-61b03ec2]{color:var(--colors-text,var(--color-text))}.correct[data-v-61b03ec2]{background:var(--color-form-valid-background);border-color:var(--color-form-valid)}.correct .choice-icon[data-v-61b03ec2]{fill:var(--color-form-valid)}.incorrect[data-v-61b03ec2]{background:var(--color-form-error-background);border-color:var(--color-form-error)}.incorrect .choice-icon[data-v-61b03ec2]{fill:var(--color-form-error)}.correct[data-v-61b03ec2],.incorrect[data-v-61b03ec2]{position:relative}.correct .choice-icon[data-v-61b03ec2],.incorrect .choice-icon[data-v-61b03ec2]{position:absolute;top:11px;left:10px;font-size:20px;width:1.05em}.disabled[data-v-61b03ec2]{pointer-events:none}.answer[data-v-61b03ec2]{margin:.5rem 1.5rem .5rem 0;font-size:.7058823529rem;line-height:1.3333333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.answer[data-v-61b03ec2]:last-of-type{margin-bottom:0}[data-v-61b03ec2] .question>.code-listing{padding:unset;border-radius:0}[data-v-61b03ec2] pre{padding:0}[data-v-61b03ec2] img{display:block;margin-left:auto;margin-right:auto;max-width:100%}.title[data-v-65e3c02c]{font-size:1.8823529412rem;line-height:1.25;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--colors-header-text,var(--color-header-text))}@media only screen and (max-width:1250px){.title[data-v-65e3c02c]{font-size:1.6470588235rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){.title[data-v-65e3c02c]{font-size:1.4117647059rem;line-height:1.3333333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.title p[data-v-65e3c02c]{color:var(--colors-text,var(--color-text))}.assessments[data-v-65e3c02c]{box-sizing:content-box;padding:0 1rem;background:var(--color-tutorial-assessments-background);margin-left:auto;margin-right:auto;width:1536px;width:980px;margin-bottom:80px}@media only screen and (max-width:1250px){.assessments[data-v-65e3c02c]{width:692px}}@media only screen and (max-width:735px){.assessments[data-v-65e3c02c]{width:87.5%}}@media only screen and (max-width:320px){.assessments[data-v-65e3c02c]{width:215px}}.banner[data-v-65e3c02c]{padding:40px 0;border-bottom:1px solid;margin-bottom:40px;border-color:var(--colors-grid,var(--color-grid));text-align:center}.success[data-v-65e3c02c]{text-align:center;padding-bottom:40px;font-size:1.8823529412rem;line-height:1.25;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--colors-text,var(--color-text))}@media only screen and (max-width:1250px){.success[data-v-65e3c02c]{font-size:1.6470588235rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){.success[data-v-65e3c02c]{font-size:1.4117647059rem;line-height:1.3333333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.assessments-wrapper[data-v-65e3c02c]{padding-top:80px}.assessments-wrapper[data-v-6db06128]{padding-bottom:40px;padding-top:0}@media only screen and (max-width:735px){.assessments-wrapper[data-v-6db06128]{padding-top:80px}}.article[data-v-1b2e3b6a]{background:var(--colors-article-background,var(--color-article-background))}@media only screen and (max-width:735px){.article[data-v-1b2e3b6a]{background:var(--colors-text-background,var(--color-article-body-background))}}.intro-container[data-v-7dcf2d10]{margin-bottom:80px}.intro[data-v-7dcf2d10]{display:flex;align-items:center}@media only screen and (max-width:735px){.intro[data-v-7dcf2d10]{padding-bottom:0;flex-direction:column}}.intro.ide .media[data-v-7dcf2d10] img{background-color:var(--colors-text-background,var(--color-text-background))}.col.left[data-v-7dcf2d10]{padding-right:40px}@media only screen and (max-width:1250px){.col.left[data-v-7dcf2d10]{padding-right:28px}}@media only screen and (max-width:735px){.col.left[data-v-7dcf2d10]{margin-left:auto;margin-right:auto;width:1536px;width:980px;padding-right:0}}@media only screen and (max-width:735px)and (max-width:1250px){.col.left[data-v-7dcf2d10]{width:692px}}@media only screen and (max-width:735px)and (max-width:735px){.col.left[data-v-7dcf2d10]{width:87.5%}}@media only screen and (max-width:735px)and (max-width:320px){.col.left[data-v-7dcf2d10]{width:215px}}.col.right[data-v-7dcf2d10]{padding-left:40px}@media only screen and (max-width:1250px){.col.right[data-v-7dcf2d10]{padding-left:28px}}@media only screen and (max-width:735px){.col.right[data-v-7dcf2d10]{padding-left:0}}.content[data-v-7dcf2d10]{font-size:1rem;line-height:1.4705882353;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.media[data-v-7dcf2d10] img{width:auto;max-height:560px;min-height:18.8235294118rem;-o-object-fit:scale-down;object-fit:scale-down}@media only screen and (max-width:735px){.media[data-v-7dcf2d10]{margin:0;margin-top:40px}.media[data-v-7dcf2d10] image,.media[data-v-7dcf2d10] video{max-height:80vh}}.media[data-v-7dcf2d10] .asset{padding:0 20px}.headline[data-v-7dcf2d10]{color:var(--colors-header-text,var(--color-header-text))}[data-v-7dcf2d10] .eyebrow{font-size:1.2352941176rem;line-height:1.1904761905;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:735px){[data-v-7dcf2d10] .eyebrow{font-size:1.1176470588rem;line-height:1.2105263158;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}[data-v-7dcf2d10] .eyebrow a{color:inherit}[data-v-7dcf2d10] .heading{font-size:1.8823529412rem;line-height:1.25;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:1250px){[data-v-7dcf2d10] .heading{font-size:1.6470588235rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){[data-v-7dcf2d10] .heading{font-size:1.4117647059rem;line-height:1.3333333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.expanded-intro[data-v-7dcf2d10]{margin-left:auto;margin-right:auto;width:1536px;width:980px;margin-top:40px}@media only screen and (max-width:1250px){.expanded-intro[data-v-7dcf2d10]{width:692px}}@media only screen and (max-width:735px){.expanded-intro[data-v-7dcf2d10]{width:87.5%}}@media only screen and (max-width:320px){.expanded-intro[data-v-7dcf2d10]{width:215px}}[data-v-7dcf2d10] .cols-2{gap:20px 16.6666666667%}[data-v-7dcf2d10] .cols-3 .column{gap:20px 12.5%}.code-preview[data-v-395e30cd]{position:sticky;overflow-y:auto;-webkit-overflow-scrolling:touch;background-color:var(--background,var(--color-step-background));height:calc(100vh - 3.05882rem)}.code-preview.ide[data-v-395e30cd]{height:100vh}.code-preview[data-v-395e30cd] .code-listing{color:var(--text,var(--color-code-plain))}.code-preview[data-v-395e30cd] .code-listing .code-line-container{padding-right:14px}.code-preview[data-v-395e30cd] pre{font-size:.7058823529rem;line-height:1.8333333333;font-weight:400;font-family:var(--typography-html-font-mono,Menlo,monospace)}.header[data-v-395e30cd]{font-size:.7058823529rem;line-height:1.3333333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);position:relative;display:flex;justify-content:space-between;align-items:center;width:-webkit-fill-available;width:-moz-available;width:stretch;cursor:pointer;font-weight:600;padding:8px 12px;border-radius:var(--border-radius,4px) var(--border-radius,4px) 0 0;z-index:1;background:var(--color-runtime-preview-background);color:var(--colors-runtime-preview-text,var(--color-runtime-preview-text))}.header[data-v-395e30cd]:focus{outline-style:none}#app.fromkeyboard .header[data-v-395e30cd]:focus{box-shadow:0 0 0 4px var(--color-focus-color);outline:none;border-color:var(--color-focus-border-color)}.runtime-preview[data-v-395e30cd]{--color-runtime-preview-shadow:rgba(0,0,0,.4);position:absolute;top:0;right:0;background:var(--color-runtime-preview-background);border-radius:var(--border-radius,4px);margin:1rem;margin-left:0;transition:width .2s ease-in;box-shadow:0 0 3px 0 var(--color-runtime-preview-shadow)}@media screen{[data-color-scheme=dark] .runtime-preview[data-v-395e30cd]{--color-runtime-preview-shadow:hsla(0,0%,100%,.4)}}@media screen and (prefers-color-scheme:dark){[data-color-scheme=auto] .runtime-preview[data-v-395e30cd]{--color-runtime-preview-shadow:hsla(0,0%,100%,.4)}}@supports not ((width:-webkit-fill-available) or (width:-moz-available) or (width:stretch)){.runtime-preview[data-v-395e30cd]{display:flex;flex-direction:column}}.runtime-preview .runtimve-preview__container[data-v-395e30cd]{border-radius:var(--border-radius,4px);overflow:hidden}.runtime-preview-ide[data-v-395e30cd]{top:0}.runtime-preview-ide .runtime-preview-asset[data-v-395e30cd] img{background-color:var(--color-runtime-preview-background)}.runtime-preview.collapsed[data-v-395e30cd]{box-shadow:0 0 3px 0 var(--color-runtime-preview-shadow);width:102px}.runtime-preview.collapsed .header[data-v-395e30cd]{border-radius:var(--border-radius,4px)}.runtime-preview.disabled[data-v-395e30cd]{box-shadow:0 0 3px 0 transparent}.runtime-preview.disabled .header[data-v-395e30cd]{color:var(--color-runtime-preview-disabled-text);cursor:auto}.runtime-preview-asset[data-v-395e30cd]{border-radius:0 0 var(--border-radius,4px) var(--border-radius,4px)}.runtime-preview-asset[data-v-395e30cd] img{border-bottom-left-radius:var(--border-radius,4px);border-bottom-right-radius:var(--border-radius,4px)}.preview-icon[data-v-395e30cd]{height:.8em;width:.8em;-webkit-user-select:none;-moz-user-select:none;user-select:none}.preview-show[data-v-395e30cd]{transform:scale(-1)}[data-v-0bdf2f26] pre{padding:10px 0}.toggle-preview[data-v-78763c14]{color:var(--color-runtime-preview-disabled-text);display:flex;align-items:center}a[data-v-78763c14]{color:var(--url,var(--color-link))}.toggle-text[data-v-78763c14]{display:flex;align-items:center}svg.toggle-icon[data-v-78763c14]{width:1em;height:1em;margin-left:.5em}.mobile-code-preview[data-v-b1691954]{background-color:var(--background,var(--color-step-background));padding:14px 0}@media only screen and (max-width:735px){.mobile-code-preview[data-v-b1691954]{display:flex;flex-direction:column}}.runtime-preview-modal-content[data-v-b1691954]{padding:45px 60px 0 60px;min-width:200px}.runtime-preview-modal-content[data-v-b1691954] img:not(.file-icon){border-radius:var(--border-radius,4px);box-shadow:0 0 3px rgba(0,0,0,.4);max-height:80vh;width:auto;display:block;margin-bottom:1rem}.runtime-preview-modal-content .runtime-preview-label[data-v-b1691954]{font-size:.7058823529rem;line-height:1.3333333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-runtime-preview-text);display:block;text-align:center;padding:.5em}[data-v-b1691954] .code-listing{color:var(--text,var(--color-code-plain))}[data-v-b1691954] .full-code-listing{padding-top:60px;min-height:calc(100vh - 60px)}[data-v-b1691954] pre{font-size:.7058823529rem;line-height:1.8333333333;font-weight:400;font-family:var(--typography-html-font-mono,Menlo,monospace)}.preview-toggle-container[data-v-b1691954]{align-self:flex-end;margin-right:20px}.step-container[data-v-d0198556]{margin:0}.step-container[data-v-d0198556]:not(:last-child){margin-bottom:100px}@media only screen and (max-width:735px){.step-container[data-v-d0198556]:not(:last-child){margin-bottom:80px}}.step[data-v-d0198556]{position:relative;border-radius:var(--tutorial-step-border-radius,var(--border-radius,4px));padding:1rem 2rem;background-color:var(--color-step-background);overflow:hidden;filter:blur(0)}.step[data-v-d0198556]:before{content:"";position:absolute;top:0;left:0;border:1px solid var(--color-step-focused);background-color:var(--color-step-focused);height:calc(100% - 2px);width:4px;opacity:0;transition:opacity .15s ease-in}.step.focused[data-v-d0198556],.step[data-v-d0198556]:focus{outline:none}.step.focused[data-v-d0198556]:before,.step[data-v-d0198556]:focus:before{opacity:1}:root.no-js .step.focused[data-v-d0198556]:before,:root.no-js .step[data-v-d0198556]:focus:before{opacity:0}@media only screen and (max-width:735px){.step[data-v-d0198556]{padding-left:2rem}.step[data-v-d0198556]:before{opacity:1}}.step-label[data-v-d0198556]{font-size:.7058823529rem;line-height:1.3333333333;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--colors-text,var(--color-step-text));margin-bottom:var(--spacing-stacked-margin-small)}.caption[data-v-d0198556]{border-top:1px solid;border-color:var(--color-step-caption);padding:1rem 0 0 0;margin-top:1rem}.media-container[data-v-d0198556]{display:none}@media only screen and (max-width:735px){.step[data-v-d0198556]{margin:0 .5882352941rem 1.1764705882rem .5882352941rem}.step.focused[data-v-d0198556],.step[data-v-d0198556]:focus{outline:none}.media-container[data-v-d0198556]{display:block;position:relative}.media-container[data-v-d0198556] img,.media-container[data-v-d0198556] video{max-height:80vh}[data-v-d0198556] .asset{padding:0 20px}}.steps[data-v-e3061a7c]{position:relative;font-size:.8235294118rem;line-height:1.4285714286;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);display:flex;color:var(--colors-text,var(--color-text))}@media only screen and (max-width:735px){.steps[data-v-e3061a7c]{padding-top:80px}.steps[data-v-e3061a7c]:before{position:absolute;top:0;border-top:1px solid var(--color-fill-gray-tertiary);content:"";width:calc(100% - 2.35294rem);margin:0 1.1764705882rem}}.steps[data-v-e3061a7c] aside{background:unset;border:unset;box-shadow:unset;-moz-column-break-inside:unset;break-inside:unset;padding:unset}.steps[data-v-e3061a7c] aside .label{font-size:.7058823529rem;line-height:1.3333333333;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.steps[data-v-e3061a7c] aside+*{margin-top:var(--spacing-stacked-margin-large)}.content-container[data-v-e3061a7c]{flex:none;margin-right:4.1666666667%;width:37.5%;margin-top:140px;margin-bottom:94vh}@media only screen and (max-width:735px){.content-container[data-v-e3061a7c]{margin-top:0;margin-bottom:0;height:100%;margin-left:0;margin-right:0;position:relative;width:100%}}.asset-container[data-v-e3061a7c]{flex:none;height:calc(100vh - 3.05882rem);background-color:var(--background,var(--color-step-background));max-width:921px;width:calc(50vw + 8.33333%);position:sticky;top:3.0588235294rem;transition:margin .1s ease-in-out}@media only screen and (max-width:767px){.asset-container[data-v-e3061a7c]{top:2.8235294118rem;height:calc(100vh - 2.82353rem)}}.asset-container[data-v-e3061a7c]:not(.for-step-code){overflow-y:auto;-webkit-overflow-scrolling:touch}.asset-container.ide[data-v-e3061a7c]{height:100vh;top:0}@media only screen and (min-width:736px){.asset-container[data-v-e3061a7c]{display:grid}.asset-container>[data-v-e3061a7c]{grid-row:1;grid-column:1;height:calc(100vh - 3.05882rem)}.asset-container.ide>[data-v-e3061a7c]{height:100vh}}.asset-container .step-asset[data-v-e3061a7c]{box-sizing:border-box;padding:0;padding-left:40px;min-height:320px;height:100%}.asset-container .step-asset[data-v-e3061a7c],.asset-container .step-asset[data-v-e3061a7c] picture{height:100%;display:flex;align-items:center}.asset-container .step-asset[data-v-e3061a7c] .video-replay-container{height:100%;display:flex;flex-direction:column;justify-content:center}.asset-container .step-asset[data-v-e3061a7c] img,.asset-container .step-asset[data-v-e3061a7c] video{width:auto;max-height:calc(100vh - 3.05882rem - 80px);max-width:531.66667px;margin:0}@media only screen and (max-width:1250px){.asset-container .step-asset[data-v-e3061a7c] img,.asset-container .step-asset[data-v-e3061a7c] video{max-width:363.66667px}}.asset-container .step-asset[data-v-e3061a7c] .video-replay-container,.asset-container .step-asset[data-v-e3061a7c] img{min-height:320px}.asset-container .step-asset[data-v-e3061a7c] .video-replay-container video{min-height:280px}.asset-container .step-asset[data-v-e3061a7c] [data-orientation=landscape]{max-width:min(841px,calc(50vw + 8.33333% - 80px))}@media only screen and (max-width:735px){.asset-container[data-v-e3061a7c]{display:none}}.asset-wrapper[data-v-e3061a7c]{width:63.2%;align-self:center;transition:transform .25s ease-out;will-change:transform}.asset-wrapper.ide .step-asset[data-v-e3061a7c] img{background-color:var(--background,var(--color-step-background))}.asset-wrapper[data-v-e3061a7c]:has([data-orientation=landscape]){width:unset}[data-v-e3061a7c] .runtime-preview-asset{display:grid}[data-v-e3061a7c] .runtime-preview-asset>*{grid-row:1;grid-column:1}.interstitial[data-v-e3061a7c]{padding:0 2rem}.interstitial[data-v-e3061a7c]:not(:first-child){margin-top:5.8823529412rem}.interstitial[data-v-e3061a7c]:not(:last-child){margin-bottom:30px}@media only screen and (max-width:735px){.interstitial[data-v-e3061a7c]{margin-left:auto;margin-right:auto;width:1536px;width:980px;padding:0}}@media only screen and (max-width:735px)and (max-width:1250px){.interstitial[data-v-e3061a7c]{width:692px}}@media only screen and (max-width:735px)and (max-width:735px){.interstitial[data-v-e3061a7c]{width:87.5%}}@media only screen and (max-width:735px)and (max-width:320px){.interstitial[data-v-e3061a7c]{width:215px}}@media only screen and (max-width:735px){.interstitial[data-v-e3061a7c]:not(:first-child){margin-top:0}}.fade-enter-active[data-v-e3061a7c],.fade-leave-active[data-v-e3061a7c]{transition:opacity .3s ease-in-out}.fade-enter[data-v-e3061a7c],.fade-leave-to[data-v-e3061a7c]{opacity:0}.section[data-v-6b3a0b3a]{padding-top:80px}.sections[data-v-79a75e9e]{margin-left:auto;margin-right:auto;width:1536px;width:980px}@media only screen and (max-width:1250px){.sections[data-v-79a75e9e]{width:692px}}@media only screen and (max-width:735px){.sections[data-v-79a75e9e]{width:87.5%}}@media only screen and (max-width:320px){.sections[data-v-79a75e9e]{width:215px}}@media only screen and (max-width:735px){.sections[data-v-79a75e9e]{margin:0;width:100%}}.tutorial[data-v-70ce4177]{background-color:var(--colors-text-background,var(--color-tutorial-background))} \ No newline at end of file diff --git a/docs/css/tutorials-overview.ddd5b228.css b/docs/css/tutorials-overview.ddd5b228.css new file mode 100644 index 0000000..4b62a93 --- /dev/null +++ b/docs/css/tutorials-overview.ddd5b228.css @@ -0,0 +1,9 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */.tutorials-navigation-link[data-v-e9f9b59c]{color:var(--color-tutorials-overview-navigation-link);transition:color .3s linear}.tutorials-navigation-link[data-v-e9f9b59c]:hover{text-decoration:none;transition:none;color:var(--color-tutorials-overview-navigation-link-hover)}.tutorials-navigation-link.active[data-v-e9f9b59c]{color:var(--color-tutorials-overview-navigation-link-active)}.tutorials-navigation-list[data-v-4e0180fa]{list-style-type:none;margin:0}.tutorials-navigation-list li+li[data-v-4e0180fa]:not(.volume--named){margin-top:24px}.tutorials-navigation-list .volume--named+.volume--named[data-v-4e0180fa]{margin-top:12px}.expand-enter-active,.expand-leave-active{transition:height .3s ease-in-out;overflow:hidden}.expand-enter,.expand-leave-to{height:0}.toggle[data-v-489416f8]{color:#f0f0f0;line-height:21px;display:flex;align-items:center;width:100%;font-weight:600;padding:6px 6px 6px 0;border-bottom:1px solid #2a2a2a;text-decoration:none;box-sizing:border-box}@media only screen and (max-width:767px){.toggle[data-v-489416f8]{padding-right:6px;border-bottom-color:hsla(0,0%,100%,.1)}}.toggle .text[data-v-489416f8]{word-break:break-word}.toggle[data-v-489416f8]:hover{text-decoration:none}.toggle .toggle-icon[data-v-489416f8]{display:inline-block;transition:transform .2s ease-in;height:.4em;width:.4em;margin-left:auto;margin-right:.2em}.collapsed .toggle .toggle-icon[data-v-489416f8]{transform:rotate(45deg)}.collapsed .toggle[data-v-489416f8],.collapsed .toggle[data-v-489416f8]:hover{color:#b0b0b0}.tutorials-navigation-menu-content[data-v-489416f8]{opacity:1;transition:height .2s ease-in,opacity .2s ease-in}.collapsed .tutorials-navigation-menu-content[data-v-489416f8]{height:0;opacity:0}.tutorials-navigation-menu-content .tutorials-navigation-list[data-v-489416f8]{padding:24px 0 12px 0}.tutorials-navigation[data-v-79093ed6]{font-size:1rem;line-height:1.2352941176;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.nav-title-content[data-v-854b4dd6]{max-width:100%}.title[data-v-854b4dd6]{color:var(--color-nav-root-title,currentColor);text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:inline-block;vertical-align:top;max-width:296px}@media only screen and (max-width:1023px){.title[data-v-854b4dd6]{max-width:205px}}@media only screen and (max-width:767px){.title[data-v-854b4dd6]{flex-basis:fill;display:initial;vertical-align:initial;max-width:none}}.subhead[data-v-854b4dd6]{color:var(--color-nav-root-subhead)}.theme-dark .subhead[data-v-854b4dd6]{color:var(--color-nav-dark-root-subhead)}.nav[data-v-54bcce6d] .nav-menu{padding-top:0}.nav[data-v-54bcce6d] .nav-menu .nav-menu-items{margin-left:auto}@media only screen and (min-width:768px){.nav[data-v-54bcce6d] .nav-menu .nav-menu-items .in-page-navigation{display:none}}@media only screen and (min-width:320px)and (max-width:735px){.nav[data-v-54bcce6d] .nav-menu .nav-menu-items{padding:18px 0 40px}}.hero[data-v-383dab71]{margin-left:auto;margin-right:auto;width:1536px;width:980px;padding-bottom:4.7058823529rem;padding-top:4.7058823529rem}@media only screen and (max-width:1250px){.hero[data-v-383dab71]{width:692px}}@media only screen and (max-width:735px){.hero[data-v-383dab71]{width:87.5%}}@media only screen and (max-width:320px){.hero[data-v-383dab71]{width:215px}}.copy-container[data-v-383dab71]{margin:0 auto;text-align:center;width:720px}.title[data-v-383dab71]{font-size:2.8235294118rem;line-height:1.0833333333;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-tutorials-overview-content)}@media only screen and (max-width:1250px){.title[data-v-383dab71]{font-size:2.3529411765rem;line-height:1.1;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){.title[data-v-383dab71]{font-size:1.8823529412rem;line-height:1.125;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.content[data-v-383dab71]{font-size:1.2352941176rem;line-height:1.380952381;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-tutorials-overview-content)}@media only screen and (max-width:735px){.content[data-v-383dab71]{font-size:1.1176470588rem;line-height:1.4210526316;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.meta[data-v-383dab71]{color:var(--color-tutorials-overview-content-alt);align-items:center;display:flex;justify-content:center}.meta-content[data-v-383dab71]{font-size:.8235294118rem;line-height:1.4285714286;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.meta .timer-icon[data-v-383dab71]{margin-right:.3529411765rem;height:16px;width:16px;fill:var(--color-tutorials-overview-icon)}@media only screen and (max-width:735px){.meta .timer-icon[data-v-383dab71]{margin-right:.2941176471rem;height:.8235294118rem;width:.8235294118rem}}.meta .time[data-v-383dab71]{font-size:1.1176470588rem;line-height:1.2105263158;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}@media only screen and (max-width:735px){.meta .time[data-v-383dab71]{font-size:1rem;line-height:1.1176470588;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.title+.content[data-v-383dab71]{margin-top:1.4705882353rem}.content+.meta[data-v-383dab71]{margin-top:1.1764705882rem}.button-cta[data-v-383dab71]{margin-top:1.7647058824rem}*+.asset[data-v-383dab71]{margin-top:4.1176470588rem}@media only screen and (max-width:1250px){.copy-container[data-v-383dab71]{width:636px}}@media only screen and (max-width:735px){.hero[data-v-383dab71]{padding-bottom:1.7647058824rem;padding-top:2.3529411765rem}.copy-container[data-v-383dab71]{width:100%}.title+.content[data-v-383dab71]{margin-top:.8823529412rem}.button-cta[data-v-383dab71]{margin-top:1.4117647059rem}*+.asset[data-v-383dab71]{margin-top:2.2352941176rem}}.image[data-v-569db166]{margin-bottom:10px}.name[data-v-569db166]{font-size:1.8823529412rem;line-height:1.125;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-tutorials-overview-header-text,#f0f0f0);word-break:break-word}@media only screen and (max-width:1250px){.name[data-v-569db166]{font-size:1.6470588235rem;line-height:1.1428571429;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){.name[data-v-569db166]{font-size:1.4117647059rem;line-height:1.1666666667;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.content[data-v-569db166]{font-size:1rem;line-height:1.2352941176;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-tutorials-overview-content-alt);margin-top:10px}.volume-name[data-v-569db166]{padding:50px 60px;text-align:center;background:var(--color-tutorials-overview-fill-secondary,#161616);margin:2px 0}@media only screen and (max-width:735px){.volume-name[data-v-569db166]{padding:40px 20px}}.document-icon[data-v-3a80772b]{margin-left:-3px}.tile[data-v-74dbeb68]{background:var(--color-tutorials-overview-fill-secondary,#161616);padding:40px 30px;color:var(--color-tutorials-overview-content-alt)}.content[data-v-74dbeb68] a,a[data-v-74dbeb68]{color:var(--colors-link,var(--color-tutorials-overview-link))}.icon[data-v-74dbeb68]{display:block;height:1.4705882353rem;line-height:1.4705882353rem;margin-bottom:.5882352941rem;width:1.4705882353rem}.icon[data-v-74dbeb68] svg.svg-icon{width:100%;max-height:100%;fill:var(--color-tutorials-overview-icon)}.icon[data-v-74dbeb68] svg.svg-icon .svg-icon-stroke{stroke:var(--color-tutorials-overview-content-alt)}.title[data-v-74dbeb68]{font-size:1.2352941176rem;line-height:1.1904761905;font-weight:600;margin-bottom:.8em}.content[data-v-74dbeb68],.link[data-v-74dbeb68],.title[data-v-74dbeb68]{font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.content[data-v-74dbeb68],.link[data-v-74dbeb68]{font-size:.8235294118rem;line-height:1.4285714286;font-weight:400}.content[data-v-74dbeb68]{color:var(--color-tutorials-overview-content-alt)}.link[data-v-74dbeb68]{display:block;margin-top:1.1764705882rem}.link .link-icon[data-v-74dbeb68]{margin-left:.2em;width:.6em;height:.6em}[data-v-74dbeb68] .inline-link{text-decoration:none}[data-v-74dbeb68] .content ul{list-style-type:none;margin-left:0;font-size:.8235294118rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}[data-v-74dbeb68] .content ul li:before{content:"​";position:absolute}[data-v-74dbeb68] .content li+li{margin-top:8px}@media only screen and (max-width:735px){.tile[data-v-74dbeb68]{padding:1.7647058824rem 1.1764705882rem}}.tile-group[data-v-4cacce0a]{display:grid;grid-column-gap:2px;grid-row-gap:2px}.tile-group.count-1[data-v-4cacce0a]{grid-template-columns:1fr;text-align:center}.tile-group.count-1[data-v-4cacce0a] .icon{margin-left:auto;margin-right:auto}.tile-group.count-2[data-v-4cacce0a]{grid-template-columns:repeat(2,1fr)}.tile-group.count-3[data-v-4cacce0a]{grid-template-columns:repeat(3,1fr)}.tile-group.count-4[data-v-4cacce0a]{grid-template-columns:repeat(2,1fr);grid-template-rows:repeat(2,auto)}.tile-group.count-5[data-v-4cacce0a]{grid-template-columns:repeat(6,1fr);grid-template-rows:repeat(2,auto)}.tile-group.count-5 .tile[data-v-4cacce0a]{grid-column-end:span 2}.tile-group.count-5 .tile[data-v-4cacce0a]:nth-of-type(-n+2){grid-column-end:span 3}.tile-group.count-6[data-v-4cacce0a]{grid-template-columns:repeat(2,1fr);grid-template-rows:repeat(3,auto)}@media only screen and (min-width:768px)and (max-width:1250px){.tile-group.tile-group[data-v-4cacce0a]{grid-template-columns:1fr;grid-template-rows:auto}}@media only screen and (max-width:735px){.tile-group.count-1[data-v-4cacce0a],.tile-group.count-2[data-v-4cacce0a],.tile-group.count-3[data-v-4cacce0a],.tile-group.count-4[data-v-4cacce0a],.tile-group.count-5[data-v-4cacce0a],.tile-group.count-6[data-v-4cacce0a]{grid-template-columns:1fr;grid-template-rows:auto}}.title[data-v-7f8022c1]{font-size:1.8823529412rem;line-height:1.125;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:#f0f0f0}@media only screen and (max-width:1250px){.title[data-v-7f8022c1]{font-size:1.6470588235rem;line-height:1.1428571429;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}@media only screen and (max-width:735px){.title[data-v-7f8022c1]{font-size:1.4117647059rem;line-height:1.1666666667;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.content[data-v-7f8022c1]{font-size:1rem;line-height:1.2352941176;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:#b0b0b0;margin-top:10px}.topic-list[data-v-0589dc3b]{list-style-type:none;margin:50px 0 0 0;position:relative}.topic-list li[data-v-0589dc3b]:before{content:"​";position:absolute}.topic-list[data-v-0589dc3b]:before{content:"";border-left:1px solid var(--color-fill-quaternary);display:block;height:calc(100% - .88235rem);left:.8823529412rem;position:absolute;top:50%;transform:translateY(-50%);width:0}.topic[data-v-0589dc3b]{font-size:1rem;line-height:1.4705882353;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);display:flex;align-items:flex-start}@media only screen and (max-width:735px){.topic[data-v-0589dc3b]{font-size:.8235294118rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}}.topic+.topic[data-v-0589dc3b]{margin-top:.5882352941rem}.topic .topic-icon[data-v-0589dc3b]{background-color:var(--color-fill-quaternary);border-radius:50%;flex-shrink:0;height:1.7647058824rem;width:1.7647058824rem;margin-right:1.1764705882rem;position:relative;text-align:center;-webkit-user-select:none;-moz-user-select:none;user-select:none;padding:.4705882353rem;box-sizing:border-box;display:flex;justify-content:center;align-items:center}.topic .topic-icon svg[data-v-0589dc3b]{fill:var(--color-tutorials-overview-icon);max-width:100%;max-height:100%;width:100%}.container[data-v-0589dc3b]{align-items:baseline;display:flex;justify-content:space-between;width:100%;padding-top:.1176470588rem}.container[data-v-0589dc3b]:hover{text-decoration:none}.container:hover .link[data-v-0589dc3b]{text-decoration:underline}.timer-icon[data-v-0589dc3b]{margin-right:.2941176471rem;height:.7058823529rem;width:.7058823529rem;fill:var(--color-tutorials-overview-icon)}.time[data-v-0589dc3b]{font-size:.8235294118rem;line-height:1.2857142857;font-weight:400;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-tutorials-overview-content-alt);align-items:center;display:inline-flex}.link[data-v-0589dc3b]{padding-right:.5882352941rem;color:var(--colors-link,var(--color-tutorials-overview-link))}@media only screen and (min-width:768px)and (max-width:1250px){.topic-list[data-v-0589dc3b]{margin-top:2.3529411765rem}}@media only screen and (max-width:735px){.topic-list[data-v-0589dc3b]{margin-top:1.7647058824rem}.topic[data-v-0589dc3b]{height:auto;align-items:flex-start}.topic.no-time-estimate[data-v-0589dc3b]{align-items:center}.topic.no-time-estimate .topic-icon[data-v-0589dc3b]{align-self:flex-start;top:0}.topic+.topic[data-v-0589dc3b]{margin-top:1.1764705882rem}.topic .topic-icon[data-v-0589dc3b]{top:.2941176471rem;margin-right:.7647058824rem}.container[data-v-0589dc3b]{flex-wrap:wrap;padding-top:0}.link[data-v-0589dc3b],.time[data-v-0589dc3b]{flex-basis:100%}.time[data-v-0589dc3b]{margin-top:.2941176471rem}}.chapter[data-v-7468bc5e]:focus{outline:none!important}.info[data-v-7468bc5e]{align-items:center;display:flex;flex-wrap:wrap;justify-content:space-between}.name[data-v-7468bc5e]{font-size:1.2352941176rem;line-height:1.1904761905;font-weight:600;font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif);color:var(--color-tutorials-overview-header-text,#f0f0f0)}.name-text[data-v-7468bc5e]{word-break:break-word}.eyebrow[data-v-7468bc5e]{font-size:1rem;line-height:1.2352941176;font-weight:400;color:var(--color-tutorials-overview-eyebrow);display:block;font-weight:600;margin-bottom:5px}.content[data-v-7468bc5e],.eyebrow[data-v-7468bc5e]{font-family:var(--typography-html-font,"Helvetica Neue","Helvetica","Arial",sans-serif)}.content[data-v-7468bc5e]{font-size:.8235294118rem;line-height:1.4285714286;font-weight:400;color:var(--color-tutorials-overview-content-alt)}.asset[data-v-7468bc5e]{flex:0 0 190px}.intro[data-v-7468bc5e]{flex:0 1 360px}@media only screen and (min-width:768px)and (max-width:1250px){.asset[data-v-7468bc5e]{flex:0 0 130px}.intro[data-v-7468bc5e]{flex:0 1 260px}}@media only screen and (max-width:767px){.intro[data-v-7468bc5e]{flex:0 1 340px}}@media only screen and (max-width:735px){.info[data-v-7468bc5e]{display:block;text-align:center}.asset[data-v-7468bc5e]{margin:0 45px}.eyebrow[data-v-7468bc5e]{margin-bottom:7px}.intro[data-v-7468bc5e]{margin-top:40px}}.tile[data-v-540dbf10]{background:var(--color-tutorials-overview-fill-secondary,#161616);margin:2px 0;padding:50px 60px}.asset[data-v-540dbf10]{margin-bottom:10px}@media only screen and (min-width:768px)and (max-width:1250px){.tile[data-v-540dbf10]{padding:40px 30px}}@media only screen and (max-width:735px){.volume[data-v-540dbf10]{border-radius:0}.tile[data-v-540dbf10]{padding:40px 20px}}.learning-path[data-v-69a72bbc]{background:var(--color-tutorials-overview-fill,#000);padding:4.7058823529rem 0}.main-container[data-v-69a72bbc]{margin-left:auto;margin-right:auto;width:1536px;width:980px;align-items:stretch;display:flex;justify-content:space-between}@media only screen and (max-width:1250px){.main-container[data-v-69a72bbc]{width:692px}}@media only screen and (max-width:735px){.main-container[data-v-69a72bbc]{width:87.5%}}@media only screen and (max-width:320px){.main-container[data-v-69a72bbc]{width:215px}}.ide .main-container[data-v-69a72bbc]{justify-content:center}.secondary-content-container[data-v-69a72bbc]{flex:0 0 200px;width:200px}.tutorials-navigation[data-v-69a72bbc]{position:sticky;top:7.7647058824rem}.primary-content-container[data-v-69a72bbc]{flex:0 1 720px;max-width:100%}.content-sections-container .content-section[data-v-69a72bbc]{border-radius:12px;overflow:hidden}.content-sections-container .content-section+.content-section[data-v-69a72bbc]{margin-top:1.1764705882rem}@media only screen and (min-width:768px)and (max-width:1250px){.learning-path[data-v-69a72bbc]{padding:2.3529411765rem 0}.primary-content-container[data-v-69a72bbc]{flex-basis:auto;margin-left:1.2941176471rem}.secondary-content-container[data-v-69a72bbc]{flex:0 0 180px;width:180px}}@media only screen and (max-width:767px){.secondary-content-container[data-v-69a72bbc]{display:none}}@media only screen and (max-width:735px){.content-sections-container .content-section[data-v-69a72bbc]{border-radius:0}.content-sections-container .content-section.volume[data-v-69a72bbc]{margin-top:1.1764705882rem}.learning-path[data-v-69a72bbc]{padding:0}.main-container[data-v-69a72bbc]{width:100%}}.tutorials-overview[data-v-40c62c57]{background:#000;flex:1;height:100%}.tutorials-overview .radial-gradient[data-v-40c62c57]{margin-top:-3.0588235294rem;padding-top:3.0588235294rem;background:var(--color-tutorials-overview-fill-secondary,var(--color-tutorials-overview-background))}@media only screen and (max-width:735px){.tutorials-overview .radial-gradient[data-v-40c62c57]{margin-top:-2.8235294118rem;padding-top:2.8235294118rem}}@-moz-document url-prefix(){.tutorials-overview .radial-gradient[data-v-40c62c57]{background:#111!important}} \ No newline at end of file diff --git a/docs/data/documentation/arinote.json b/docs/data/documentation/arinote.json new file mode 100644 index 0000000..accd522 --- /dev/null +++ b/docs/data/documentation/arinote.json @@ -0,0 +1 @@ +{"metadata":{"roleHeading":"Welcome to","modules":[{"name":"Ari's Notes"}],"symbolKind":"module","role":"collection","title":"Ari's Notes","externalID":"AriNote"},"variants":[{"paths":["\/documentation\/arinote"],"traits":[{"interfaceLanguage":"swift"}]}],"schemaVersion":{"minor":3,"patch":0,"major":0},"sections":[],"topicSections":[{"identifiers":["doc:\/\/AriNote\/documentation\/AriNote\/CS","doc:\/\/AriNote\/documentation\/AriNote\/Git","doc:\/\/AriNote\/documentation\/AriNote\/Rx","doc:\/\/AriNote\/documentation\/AriNote\/Swift","doc:\/\/AriNote\/documentation\/AriNote\/SwiftUI","doc:\/\/AriNote\/documentation\/AriNote\/UIKit","doc:\/\/AriNote\/documentation\/AriNote\/Xcode","doc:\/\/AriNote\/documentation\/AriNote\/Architecture"],"anchor":"Topics"}],"primaryContentSections":[{"kind":"content","content":[{"level":2,"type":"heading","text":"Overview","anchor":"overview"},{"inlineContent":[{"type":"text","text":"공부한 것을 기록합니다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"내가 설명하지 못하는 것은 내가 아는 것이 아니다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사람은 망각의 동물이므로 내가 오늘 배운 내용을 금세 잊어버린다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"내가 쓴 글은 제목만 보더라도 내용이 금세 기억나는 마법을 경험할 수 있다."}]}]}]},{"name":"Note","content":[{"items":[{"content":[{"inlineContent":[{"type":"text","text":"정확하지 않은 내용이 있을 수도 있습니다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"공부하면서 메모한 내용들이라 완벽하지 않습니다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"공부한 흔적을 생생하게 남기는 목적으로 기록하고 있습니다."}],"type":"paragraph"}]}],"type":"unorderedList"}],"style":"note","type":"aside"},{"anchor":"History","type":"heading","text":"History","level":2},{"items":[{"content":[{"inlineContent":[{"code":"2021-08","type":"codeVoice"},{"text":": 야곰 아카데미 코드 스타터 캠프 기록","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"code":"2021-10 ~ 2022-03","type":"codeVoice"},{"text":": 야곰 아카데미 커리어 스타터 캠프 기록","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"text":"Template","level":2,"anchor":"Template","type":"heading"},{"type":"codeListing","syntax":null,"code":["# 날짜 키워드","","1월 1일 (월)","","# 학습내용","","- 내용","","# 고민한 점 \/ 해결방법","","- 내용","","# 느낀점","","- 내용","","---","","# 참고 링크","","- 링크"]}]}],"abstract":[{"type":"codeVoice","code":"Today I Learned"}],"hierarchy":{"paths":[[]]},"kind":"symbol","identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote"},"references":{"doc://AriNote/documentation/AriNote/Architecture":{"role":"collectionGroup","kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Architecture","abstract":[{"text":"공부 많이해서 상황에 따라 적절한 아키텍처를 선택해보자.","type":"text"}],"title":"Architecture","type":"topic","url":"\/documentation\/arinote\/architecture"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"doc://AriNote/documentation/AriNote/Xcode":{"role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Xcode","url":"\/documentation\/arinote\/xcode","title":"Xcode","kind":"article","abstract":[{"text":"언젠가…. 좋아질거지 Xcode…?","type":"text"}],"type":"topic"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]},"doc://AriNote/documentation/AriNote/Git":{"abstract":[{"type":"text","text":"Git 관련 지식"}],"type":"topic","title":"Git","url":"\/documentation\/arinote\/git","kind":"article","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Git"},"doc://AriNote/documentation/AriNote/SwiftUI":{"role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/SwiftUI","url":"\/documentation\/arinote\/swiftui","title":"SwiftUI","kind":"article","abstract":[{"type":"text","text":"언능 UIKit만큼 SwiftUI를 더 잘 알고 싶다!"}],"type":"topic"},"doc://AriNote/documentation/AriNote/Rx":{"type":"topic","abstract":[{"type":"text","text":"RxSwift를 활용한 비동기 프로그래밍"}],"role":"collectionGroup","url":"\/documentation\/arinote\/rx","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Rx","title":"RxSwift","kind":"article"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/210802_---_--.json b/docs/data/documentation/arinote/210802_---_--.json new file mode 100644 index 0000000..b9651a0 --- /dev/null +++ b/docs/data/documentation/arinote/210802_---_--.json @@ -0,0 +1 @@ +{"variants":[{"paths":["\/documentation\/arinote\/210802_---_--"],"traits":[{"interfaceLanguage":"swift"}]}],"kind":"article","metadata":{"roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}],"title":"210802 정수와 실수"},"schemaVersion":{"major":0,"patch":0,"minor":3},"sections":[],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","type":"heading","anchor":"TIL-Today-I-Learned","level":1},{"type":"paragraph","inlineContent":[{"type":"text","text":"날짜: 2021년 8월 2일"}]},{"anchor":"%ED%95%99%EC%8A%B5%EB%82%B4%EC%9A%A9","text":"학습내용","level":2,"type":"heading"},{"inlineContent":[{"type":"text","text":"오늘은 컴퓨터의 대한 정보적 사고와 컴퓨터의 역사에 대해 학습하였다. 이후 토론 주제중 3번과 4번을 골라 작성하기 위해 정수 표현법, 실수표현법에 대한 것과 부호타입에 대하여 공부하였다. 정수는 부호를 포함하여 8비트로 표현해야 하지만 실수는 표현방식이 정수랑은 다를 뿐더러 두가지로 나뉜다. 부호타입은 부호비트와 2의 보수로 표현이 가능하다. 평소 컴퓨터의 대한 기초 지식이 부족하여 토론 주제에 대해 작성하려면 정수와 실수는 어떤 방식으로 표현하는지? 그리고 부호타입은 왜 구분하는 것인지? 모두 상세하게 공부해야 했었다. 토론 주제 중 3번과 4번을 고른 이유는 해당 주제에 대해 내가 알고있는 정확한 지식이 별로 없어서였다. 이번 계기로 상세하게 공부하게 되었고 해당 학습을 통해 토론 작성에 많은 도움이 되었다!"}],"type":"paragraph"},{"level":2,"text":"정수 표현 방식과 실수 표현 방식","anchor":"%EC%A0%95%EC%88%98-%ED%91%9C%ED%98%84-%EB%B0%A9%EC%8B%9D%EA%B3%BC-%EC%8B%A4%EC%88%98-%ED%91%9C%ED%98%84-%EB%B0%A9%EC%8B%9D","type":"heading"},{"items":[{"content":[{"inlineContent":[{"text":"정수를 표현하는 기본방식","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/user-attachments\/assets\/24a41a55-c304-4af6-a26c-61a4a2572205","type":"image"}]},{"inlineContent":[{"type":"text","text":"정수형 데이터를 1byte 크기로 표현한다면 sign bit 1개를 포함하여 8bit로 표현해야한다. 부호를 표시하는 정수 표현 방법과 부호를 표현하지 않는 정수 표현 방법 2가지 형태로 표현될 수 있다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"실수를 표현하는 기본 방식"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/user-attachments\/assets\/97659440-be11-49ef-aa29-defa5f18091f","type":"image"}]},{"type":"paragraph","inlineContent":[{"text":"고정 소수점 표기법: 고정되어 있는 실수 표현법으로 부호, 정수부 소수부를 표현하는 3부분으로 나뉘고 이 크기가 고정되어 있다. 이런 이유로 아주 큰 수나 아주 작은 수를 표현하기에는 적합하지 않다. (실수 표현 범위가 넓지 않아서) 그러나 부동 소수점 방식과 다르게 밑수 뿐만 아니라 지수 값도 미리 결정해 두고 사용하는 방식이기 때문에 정수형 자료형을 기반으로 소수점을 표현할 수 있다. 연산 장치에 따라서 부동 소수점을 지원하지 않는 경우에는 이러한 소수점을 표현해야 한다. 또한 정수형 자료형의 연산은 부동 소수점 자료형 연산보다 빠르게 할 수 있다는 장점이 있다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/48d80064-2879-4a25-b49d-fabceb1e014c"}]},{"inlineContent":[{"type":"text","text":"부동 소수점 표기법: IEEE 754 표현법을 따르며 크게 부호비트, 지수, 가수를 표현하는 부분으로 나뉘고 지수값에 소수점이 이동하면서 큰 값, 작은값을 표현할 수 있게 된다. 그러나 고정 소수점 방식 보다 연산 속도가 느리기 때문에 전용 연산 장치를 두는 경우가 많다. 고정 소수점과 달리 정수 부분과 소수 부분의 자릿수가 일정하지 않으나 유효 숫자의 자릿수는 정해져 있다."}],"type":"paragraph"},{"level":2,"anchor":"%EC%BB%B4%ED%93%A8%ED%84%B0%EC%9D%98-%EC%9D%8C%EC%88%98-%ED%91%9C%ED%98%84%EB%B2%95","text":"컴퓨터의 음수 표현법","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"부호비트","type":"text"}]}]}]},{"inlineContent":[{"text":"정수를 표현하는데 있어서 가장 왼쪽에 존재하는 비트는 부호를 표현하는데 사용된다. 이 비트를 가리켜 ","type":"text"},{"type":"strong","inlineContent":[{"type":"text","text":"MSB"}]},{"text":"(Most Significant Bit)라고 하는데, 가장 중요한 비트라는 뜻을 지닌다. 이 비트의 설정에 따라 정수의 부호가 달라지게 된다. 그러나 양수와 음수간의 연산이 어렵다는 단점이 있어 실수 방식의 표현에만 쓰인다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"1의 보수"}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"text":"컴퓨터에서의 1의 보수는 우리가 알고 있는 것과 개념이 약간 다르다. 1의 보수의 개념은 1과 0을 반전시켜주는 역할을 한다. 하지만 1의 보수만을 사용하여 연산을 할 시 비트반전을 너무 많이 해야한다는 단점이 있다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"2의 보수"}],"type":"paragraph"}]}]},{"inlineContent":[{"type":"text","text":"2의 보수는 1의 보수에 1을 더해주었을때의 결과값을 의미한다."}],"type":"paragraph"},{"level":2,"text":"학습에 참고한 링크","type":"heading","anchor":"%ED%95%99%EC%8A%B5%EC%97%90-%EC%B0%B8%EA%B3%A0%ED%95%9C-%EB%A7%81%ED%81%AC"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"isActive":true,"identifier":"https:\/\/gamedevlog.tistory.com\/23?category=894993","type":"reference"}],"type":"strong"}]},{"inlineContent":[{"identifier":"https:\/\/gamedevlog.tistory.com\/m\/24?category=894993","type":"reference","isActive":true}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/ko.wikipedia.org\/wiki\/%EA%B3%A0%EC%A0%95%EC%86%8C%EC%88%98%EC%A0%90","type":"reference","isActive":true}]},{"inlineContent":[{"identifier":"https:\/\/ko.wikipedia.org\/wiki\/%EB%B6%80%EB%8F%99%EC%86%8C%EC%88%98%EC%A0%90","type":"reference","isActive":true}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"isActive":true,"identifier":"https:\/\/ko.wikipedia.org\/wiki\/2%EC%9D%98_%EB%B3%B4%EC%88%98","type":"reference"}]},{"inlineContent":[{"type":"reference","identifier":"https:\/\/zapiro.tistory.com\/entry\/%EC%BB%B4%ED%93%A8%ED%84%B0%EC%9D%98-%EC%9D%8C%EC%88%98-%ED%91%9C%ED%98%84%EB%B2%95%EB%B3%B4%EC%88%98%EB%B2%95","isActive":true}],"type":"paragraph"},{"inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/youtu.be\/4qH4unVtJkE"}],"type":"paragraph"}]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/210802_---_--","interfaceLanguage":"swift"},"references":{"https://zapiro.tistory.com/entry/%EC%BB%B4%ED%93%A8%ED%84%B0%EC%9D%98-%EC%9D%8C%EC%88%98-%ED%91%9C%ED%98%84%EB%B2%95%EB%B3%B4%EC%88%98%EB%B2%95":{"titleInlineContent":[{"type":"text","text":"https:\/\/zapiro.tistory.com\/entry\/%EC%BB%B4%ED%93%A8%ED%84%B0%EC%9D%98-%EC%9D%8C%EC%88%98-%ED%91%9C%ED%98%84%EB%B2%95%EB%B3%B4%EC%88%98%EB%B2%95"}],"url":"https:\/\/zapiro.tistory.com\/entry\/%EC%BB%B4%ED%93%A8%ED%84%B0%EC%9D%98-%EC%9D%8C%EC%88%98-%ED%91%9C%ED%98%84%EB%B2%95%EB%B3%B4%EC%88%98%EB%B2%95","type":"link","title":"https:\/\/zapiro.tistory.com\/entry\/%EC%BB%B4%ED%93%A8%ED%84%B0%EC%9D%98-%EC%9D%8C%EC%88%98-%ED%91%9C%ED%98%84%EB%B2%95%EB%B3%B4%EC%88%98%EB%B2%95","identifier":"https:\/\/zapiro.tistory.com\/entry\/%EC%BB%B4%ED%93%A8%ED%84%B0%EC%9D%98-%EC%9D%8C%EC%88%98-%ED%91%9C%ED%98%84%EB%B2%95%EB%B3%B4%EC%88%98%EB%B2%95"},"https://gamedevlog.tistory.com/23?category=894993":{"url":"https:\/\/gamedevlog.tistory.com\/23?category=894993","title":"https:\/\/gamedevlog.tistory.com\/23?category=894993","titleInlineContent":[{"type":"text","text":"https:\/\/gamedevlog.tistory.com\/23?category=894993"}],"identifier":"https:\/\/gamedevlog.tistory.com\/23?category=894993","type":"link"},"https://ko.wikipedia.org/wiki/%EA%B3%A0%EC%A0%95%EC%86%8C%EC%88%98%EC%A0%90":{"titleInlineContent":[{"type":"text","text":"https:\/\/ko.wikipedia.org\/wiki\/고정소수점"}],"url":"https:\/\/ko.wikipedia.org\/wiki\/%EA%B3%A0%EC%A0%95%EC%86%8C%EC%88%98%EC%A0%90","type":"link","title":"https:\/\/ko.wikipedia.org\/wiki\/고정소수점","identifier":"https:\/\/ko.wikipedia.org\/wiki\/%EA%B3%A0%EC%A0%95%EC%86%8C%EC%88%98%EC%A0%90"},"https://ko.wikipedia.org/wiki/%EB%B6%80%EB%8F%99%EC%86%8C%EC%88%98%EC%A0%90":{"url":"https:\/\/ko.wikipedia.org\/wiki\/%EB%B6%80%EB%8F%99%EC%86%8C%EC%88%98%EC%A0%90","title":"https:\/\/ko.wikipedia.org\/wiki\/부동소수점","titleInlineContent":[{"type":"text","text":"https:\/\/ko.wikipedia.org\/wiki\/부동소수점"}],"identifier":"https:\/\/ko.wikipedia.org\/wiki\/%EB%B6%80%EB%8F%99%EC%86%8C%EC%88%98%EC%A0%90","type":"link"},"https://github.com/user-attachments/assets/24a41a55-c304-4af6-a26c-61a4a2572205":{"alt":"ex_screenshot","variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/24a41a55-c304-4af6-a26c-61a4a2572205","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/24a41a55-c304-4af6-a26c-61a4a2572205"},"https://github.com/user-attachments/assets/97659440-be11-49ef-aa29-defa5f18091f":{"variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/97659440-be11-49ef-aa29-defa5f18091f"}],"alt":"ex_screenshot","identifier":"https:\/\/github.com\/user-attachments\/assets\/97659440-be11-49ef-aa29-defa5f18091f","type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://youtu.be/4qH4unVtJkE":{"titleInlineContent":[{"type":"text","text":"https:\/\/youtu.be\/4qH4unVtJkE"}],"url":"https:\/\/youtu.be\/4qH4unVtJkE","type":"link","title":"https:\/\/youtu.be\/4qH4unVtJkE","identifier":"https:\/\/youtu.be\/4qH4unVtJkE"},"https://ko.wikipedia.org/wiki/2%EC%9D%98_%EB%B3%B4%EC%88%98":{"url":"https:\/\/ko.wikipedia.org\/wiki\/2%EC%9D%98_%EB%B3%B4%EC%88%98","title":"https:\/\/ko.wikipedia.org\/wiki\/2%EC%9D%98_%EB%B3%B4%EC%88%98","titleInlineContent":[{"type":"text","text":"https:\/\/ko.wikipedia.org\/wiki\/2%EC%9D%98_%EB%B3%B4%EC%88%98"}],"identifier":"https:\/\/ko.wikipedia.org\/wiki\/2%EC%9D%98_%EB%B3%B4%EC%88%98","type":"link"},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]},"https://gamedevlog.tistory.com/m/24?category=894993":{"titleInlineContent":[{"type":"text","text":"https:\/\/gamedevlog.tistory.com\/m\/24?category=894993"}],"url":"https:\/\/gamedevlog.tistory.com\/m\/24?category=894993","type":"link","title":"https:\/\/gamedevlog.tistory.com\/m\/24?category=894993","identifier":"https:\/\/gamedevlog.tistory.com\/m\/24?category=894993"},"https://github.com/user-attachments/assets/48d80064-2879-4a25-b49d-fabceb1e014c":{"variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/48d80064-2879-4a25-b49d-fabceb1e014c"}],"alt":"ex_screenshot","identifier":"https:\/\/github.com\/user-attachments\/assets\/48d80064-2879-4a25-b49d-fabceb1e014c","type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/210803_for-in_-_foreach.json b/docs/data/documentation/arinote/210803_for-in_-_foreach.json new file mode 100644 index 0000000..a52e4c7 --- /dev/null +++ b/docs/data/documentation/arinote/210803_for-in_-_foreach.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/210803_for-in_-_foreach"]}],"primaryContentSections":[{"content":[{"type":"heading","text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned","level":1},{"inlineContent":[{"type":"text","text":"날짜: 2021년 8월 3일"},{"text":" ","type":"text"},{"type":"text","text":"작성자: 이아리"},{"text":" ","type":"text"},{"type":"text","text":"태그: break, continue, forEach, forloop, return, 반복문, 제어문"}],"type":"paragraph"},{"anchor":"%ED%95%99%EC%8A%B5%EB%82%B4%EC%9A%A9","type":"heading","text":"학습내용","level":2},{"type":"paragraph","inlineContent":[{"type":"text","text":"오늘은 Swift 문법과 많이 친해지기 위해 코딩테스트 연습을 하였다. 하다가 ‘forEach’ 라는 것은 for in과의 차이점이 무엇인지 갑자기 궁금해져서 어떤 차이점이 있는지 알아보았다. for in문은 반복제어문을 사용하여 클로저 탈출이 가능하지만, forEach는 제어문 사용이 아예 불가능했다. 알아보니 forEach는 반복문이 아니라 Closure를 파라미터로 넘겨주는 ‘메서드’ 였던 것이다. 동작은 for in문과 같은 반복을 하는 것을 알고 있었지만, 이번 기회에 정확한 사용법과 어떤 차이점이 있는지 학습하였다."}]},{"level":2,"text":"코드 적어보기","type":"heading","anchor":"%EC%BD%94%EB%93%9C-%EC%A0%81%EC%96%B4%EB%B3%B4%EA%B8%B0"},{"type":"paragraph","inlineContent":[{"type":"text","text":"✔️ 두 반복문은 같은 결과를 출력한다."}]},{"code":["let arr = [\"leeari\", \"ari\", \"lee\"]","","for i in arr {"," print(i)","}","arr.forEach { i in"," print(i)","}"],"type":"codeListing","syntax":"swift"},{"type":"paragraph","inlineContent":[{"type":"text","text":"✔️ 그러나 for in문에서는 사용이 가능한 제어문이 forEach에서는 에러가 뜨며 사용이 불가능하다."}]},{"syntax":"swift","code":["for i in arr {"," if i == \"leeari\" {"," continue"," }"," if i == \"ari\" {"," break"," }","}","arr.forEach{ i in","\/\/ !!! error: only allowed inside a loop !!!","\/\/ break","\/\/ continue","}"],"type":"codeListing"},{"type":"paragraph","inlineContent":[{"text":"✔️ for in문은 return문을 만나면 반복이 즉시 종료되고 함수가 종료되지만, forEach는 반복횟수에 영향이 없다.","type":"text"}]},{"code":["func loop() {"," let arr = [\"leeari\", \"ari\", \"lee\"]"," "," for i in arr {"," if i == \"ari\" {"," print(i)"," return"," }"," }","}","arr.forEach{ i in"," if i == \"ari\" {"," return print(i)"," }"," print(i)","}"],"type":"codeListing","syntax":"swift"},{"text":"정리","level":2,"anchor":"%EC%A0%95%EB%A6%AC","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"for in문은 break과 contiue문을 사용하여 클로저를 탈출할 수 있지만, forEach는 이러한 "},{"inlineContent":[{"type":"text","text":"제어문 사용이 불가능"}],"type":"strong"},{"text":"하다. forEach는 내가 반복하고 싶은 구문을 forEach라는 함수의 파라미터로 Closure로 작성하여 넘겨주기 때문이다.","type":"text"}]},{"text":"학습에 참고한 링크","type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5%EC%97%90-%EC%B0%B8%EA%B3%A0%ED%95%9C-%EB%A7%81%ED%81%AC"},{"type":"paragraph","inlineContent":[{"type":"reference","isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/array\/1689783-foreach"}]}],"kind":"content"}],"schemaVersion":{"major":0,"minor":3,"patch":0},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/210803_for-in_-_forEach","interfaceLanguage":"swift"},"metadata":{"roleHeading":"Article","role":"article","title":"210803 for-in 외 forEach","modules":[{"name":"Ari's Notes"}]},"kind":"article","sections":[],"references":{"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://developer.apple.com/documentation/swift/array/1689783-foreach":{"type":"link","url":"https:\/\/developer.apple.com\/documentation\/swift\/array\/1689783-foreach","title":"https:\/\/developer.apple.com\/documentation\/swift\/array\/1689783-foreach","titleInlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/swift\/array\/1689783-foreach"}],"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/array\/1689783-foreach"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/210804_-------.json b/docs/data/documentation/arinote/210804_-------.json new file mode 100644 index 0000000..3a4bbee --- /dev/null +++ b/docs/data/documentation/arinote/210804_-------.json @@ -0,0 +1 @@ +{"sections":[],"variants":[{"paths":["\/documentation\/arinote\/210804_-------"],"traits":[{"interfaceLanguage":"swift"}]}],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/210804_-------"},"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"metadata":{"roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"role":"article","title":"210804 동적프로그래밍"},"schemaVersion":{"patch":0,"minor":3,"major":0},"primaryContentSections":[{"kind":"content","content":[{"type":"heading","level":1,"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)"},{"inlineContent":[{"text":"날짜: 2021년 8월 4일","type":"text"},{"type":"text","text":" "},{"text":"작성자: 이아리","type":"text"},{"type":"text","text":" "},{"type":"text","text":"태그: dp, 동적프로그래밍, 시간복잡도, 알고리즘"}],"type":"paragraph"},{"text":"학습내용","type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5%EB%82%B4%EC%9A%A9"},{"inlineContent":[{"type":"text","text":"동적 프로그래밍이란것은 무엇인가? 이름만 잠깐 줏어봤지.. 개념에 대해서 깊고 파고든 적이 없는 것 같아 이번에 동적 프로그래밍에 대한것을 파헤치기로 결심했다. 계기는 코딩테스트 문제중에 dp를 활용하여 2차원배열의 합을 구하는 문제가 있었는데, 반복문으로 손쉽게 풀어낼수도 있는 문제였지만, 내가 풀려고 할땐 좀처럼 쉽게 풀리지 않았다. 끙끙 앓다가 다른사람 코드를 참고해보니 dp를 이용해서 반복문보다 훨씬 빠른속도로 풀어낼 수 있다는 사실을 알았다. 반복문으로 손쉽게 풀어낼수도 있었지만 dp란 무엇인지? 어떻게 동작하는지? 좀더 정확하게 개념을 파악하고 싶었다. 무엇보다 코드만 보고서는 이해가 잘 되지 않았고, 다른사람이 dp에 대해 풀이를 해둔 글이라던지, 설명들이 좀처럼 나는 이해가 잘 안갔다. 그래서 내가 글로 정리하면서 한단계씩 적어나가며 어떻게 다뤄야 하는지에 대해서 풀어보려고 했다."}],"type":"paragraph"},{"text":"정리","anchor":"%EC%A0%95%EB%A6%AC","level":2,"type":"heading"},{"type":"paragraph","inlineContent":[{"type":"reference","isActive":true,"identifier":"https:\/\/leeari95.tistory.com\/12"}]},{"type":"paragraph","inlineContent":[{"text":"정리한 내용이 너무 길어서 따로 정리했습니다. 블로그 링크를 걸어두겠습니다.","type":"text"}]},{"inlineContent":[{"type":"text","text":"만든지가 얼마 안된 블로그라 볼건 별로없어요..☺️"}],"type":"paragraph"},{"text":"참고하시는 분들은 주의하세요.","anchor":"%EC%B0%B8%EA%B3%A0%ED%95%98%EC%8B%9C%EB%8A%94-%EB%B6%84%EB%93%A4%EC%9D%80-%EC%A3%BC%EC%9D%98%ED%95%98%EC%84%B8%EC%9A%94","level":2,"type":"heading"},{"type":"paragraph","inlineContent":[{"text":"해당 포스트에는 해당 문제의 답안이 ‘접은글’ 상태로 있으니 문제를 풀어보기 전에는 열어보지 않는 것을 권장드립니다!","type":"text"}]}]}],"references":{"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]},"https://leeari95.tistory.com/12":{"url":"https:\/\/leeari95.tistory.com\/12","title":"https:\/\/leeari95.tistory.com\/12","type":"link","titleInlineContent":[{"text":"https:\/\/leeari95.tistory.com\/12","type":"text"}],"identifier":"https:\/\/leeari95.tistory.com\/12"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/210805_api_design_guidelines.json b/docs/data/documentation/arinote/210805_api_design_guidelines.json new file mode 100644 index 0000000..8dc2f76 --- /dev/null +++ b/docs/data/documentation/arinote/210805_api_design_guidelines.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","level":1,"text":"TIL (Today I Learned)","type":"heading"},{"type":"paragraph","inlineContent":[{"text":"날짜: 2021년 8월 5일","type":"text"},{"type":"text","text":" "},{"type":"text","text":"작성자: 이아리"},{"type":"text","text":" "},{"type":"text","text":"태그: API, UpperCamelCase, lowerCamelCase, 동사, 명사, 변수명, 함수명"}]},{"text":"학습내용","level":2,"type":"heading","anchor":"%ED%95%99%EC%8A%B5%EB%82%B4%EC%9A%A9"},{"type":"paragraph","inlineContent":[{"type":"text","text":"오늘은 캠프 1주차에서 다뤘었던 API Design Guidelines에 대해서 다시 한번 복습해보는 시간을 가졌다. 한번만 읽고 지나가는 것 보다는 정리해놓고 자주 훑어보는 것이 좋을 것 같다는 생각이 들었다. 평소 해당 개념에 대해서 꼼꼼하고 상세하게 다뤄보지 못한 것 같아 이번 기회에 상세히 알아보며 기본개념과 Naming에 대해서 정리해보기로 하였다."}]},{"type":"thematicBreak"},{"anchor":"Fundamentals-%EA%B8%B0%EB%B3%B8%EA%B0%9C%EB%85%90","text":"Fundamentals (기본개념)","type":"heading","level":2},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"가장 중요한 목표는 사용 시점에서의 명확성(Clarity)이다. 메소드, 속성같은 개체들을 이해하기 쉽고 간결하게 만드는데 중점을 두고 작성해야 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"명확성(Clarity)은 간결성(brevity)보다 중요하다. 문자들 몇 개만 사용해서 적은 양의 코드를 작성하는 것이 목표가 아니다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"모든 선언문에 문서화용 주석(documentation comments)을 작성해야한다. 문서를 작성하면서 얻는 인사이트가 자신의 설계에 깊은 영향을 줄 수 있으니 미루지 말자."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Xcode 자동완성에서 볼 수 있도록 마크 다운을 적극 활용하라."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"선언한 요소에 대해 설명하는 요약으로 시작하라. API설계에 대한 평가는 선언 부분과 요약만으로도 완벽하게 이해할 수 있다."}]}]}],"type":"orderedList"},{"type":"thematicBreak"},{"type":"heading","level":2,"text":"Naming (이름 짓기)","anchor":"Naming-%EC%9D%B4%EB%A6%84-%EC%A7%93%EA%B8%B0"},{"type":"paragraph","inlineContent":[{"text":"명확한 사용 활성화하기 (Promote Clear Usage)","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"그 이름을 사용하는 부분의 코드를 읽는 사람에게 혼란을 줄 수 있는 단어는 피하라."}],"type":"paragraph"}]}],"type":"unorderedList"},{"syntax":"swift","type":"codeListing","code":["\/\/ [좋은 예]","extension List {"," public mutating func remove(at position: Index) -> Element","}","employees.remove(at: x)"]},{"inlineContent":[{"text":"메소드 시그니처에서 at을 생략한다면 해당 메소드가 x과 같은 요소를 제거하는 건지, x위체에 있는 요소를 찾아서 제거한다는 건지 헷갈릴 수 있다.","type":"text"}],"type":"paragraph"},{"code":["\/\/ [나쁜 예]","employees.remove(x) \/\/ unclear: are we removing x?"],"type":"codeListing","syntax":"swift"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"불필요한 단어를 제거하라. 이름의 모든 단어는 사용자 관점에서 주요한 정보를 제공해야만 한다.","type":"text"}]}]}]},{"syntax":"swift","code":["\/\/ [나쁜 예]","public mutating func removeElement(member: Element) -> Element?","","allViews.removeElement(cancelButton)"],"type":"codeListing"},{"type":"paragraph","inlineContent":[{"text":"더 많은 단어를 사용하면 의도가 명확해지고 헷갈리지 않을 수 있지만 코드를 읽는 사람에게 중복된 정보를 제공하는 경우는 제거해야 한다. 위에 코드에서 element는 호출하는 지점에서는 의미가 없으니 다음과 같은 코드가 더 좋다.","type":"text"}]},{"type":"codeListing","code":["\/\/ [좋은 예]","public mutating func remove(member: Element) -> Element?","","allViews.remove(cancelButton) \/\/ clearer"],"syntax":"swift"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"변수, 매개변수 연관타입은 선언한 타입이나 제약사항 보다는 역할에 맞는 이름을 갖도록 한다."}]}]}],"type":"unorderedList"},{"code":["\/\/ [나쁜 예]","var string = \"Hello\"","protocol ViewController {"," associatedtype ViewType : View","}","class ProductionLine {"," func restock(from widgetFactory: WidgetFactory)","}"],"syntax":"swift","type":"codeListing"},{"inlineContent":[{"text":"이처럼 타입이름을 반복해서 사용하는 것도 표현성이나 명료성은 헤치는 요소다. 대신 역할을 표현하는 이름이 더 좋다.","type":"text"}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["\/\/[좋은 예]","var greeting = \"Hello\"","protocol ViewController {"," associatedtype ContentView : View","}","class ProductionLine {"," func restock(from supplier: WidgetFactory)","}"]},{"type":"paragraph","inlineContent":[{"type":"text","text":"연관 타입도 제네릭 타입 이름 뒤에 Type을 붙이는 것을 피하라."}]},{"code":["protocol Sequence {"," associatedtype IteratorType : Iterator","}"],"syntax":"swift","type":"codeListing"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"매개변수 역할을 명확하게 넣어서 부족한 타입 정보를 보완하라. 매개변수 타입이 NSObject, Any, AnyObject 이거나 Int나 String 같은 기본 타입이면, 사용하는 지점에서 맥락상 타입 정보가 불명확할 수 있다.","type":"text"}]}]}]},{"code":["\/\/ [나쁜 예]","func add(observer: NSObject, for keyPath: String)","","grid.add(self, for: graphics) \/\/ 불분명함"],"type":"codeListing","syntax":"swift"},{"inlineContent":[{"text":"명확성을 갖도록 부족한 타입 정보 마다 역할을 설명하는 명사를 붙여준다.","type":"text"}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["\/\/ [좋은 예]","func addObserver(_ observer: NSObject, forKeyPath path: String)","grid.addObserver(self, forKeyPath: graphics) \/\/ 명확함"]},{"type":"thematicBreak"},{"text":"말하는 것처럼 술술 써지도록 작성하기(Strive for Fluent Usage)","anchor":"%EB%A7%90%ED%95%98%EB%8A%94-%EA%B2%83%EC%B2%98%EB%9F%BC-%EC%88%A0%EC%88%A0-%EC%8D%A8%EC%A7%80%EB%8F%84%EB%A1%9D-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0Strive-for-Fluent-Usage","level":3,"type":"heading"},{"items":[{"content":[{"inlineContent":[{"text":"메서드와 함수 이름을 사용할 때 영어 문장을 작성하는 것처럼 느끼도록 형태로 제공해라.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["\/\/ [좋은 예]","x.insert(y, at: z) “x에서 z에다 y를 삽입”","x.subViews(havingColor: y) “색상 y을 갖는 x의 subviews”","x.capitalizingNouns() “x에 명사를 대문자화”"]},{"syntax":"swift","type":"codeListing","code":["\/\/ [나쁜 예]","x.insert(y, position: z)","x.subViews(color: y)","x.nounCapitalize()"]},{"syntax":"swift","code":["AudioUnit.instantiate("," with: description, "," options: [.inProcess], completionHandler: stopProgressBar)"],"type":"codeListing"},{"type":"paragraph","inlineContent":[{"text":"첫 번째나 두 번째 인자값 다음에 전달하는 값이 중요하지 않는 경우는 예외적으로 생략해도 된다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"inlineContent":[{"type":"text","text":"팩토리 메서드 이름은 “make”로 시작하라."}],"type":"strong"},{"type":"text","text":" 예시) x.makeIterator()"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"초기화 메서드나 팩토리 메서드에서 첫번째 인자에 추가적인 설명을 포함하지 않도록 한다."}]},{"type":"text","text":" 예시) x.makeWidget(cogCount: 47)"}]}]}],"type":"unorderedList"},{"code":["\/\/ [좋은 예]","let foreground = Color(red: 32, green: 64, blue: 128)","let newPart = factory.makeWidget(gears: 42, spindles: 14)","let ref = Link(target: destination)"],"syntax":"swift","type":"codeListing"},{"syntax":"swift","type":"codeListing","code":["\/\/ [나쁜 예]","let foreground = Color(havingRGBValuesRed: 32, green: 64, andBlue: 128)","let newPart = factory.makeWidget(havingGearCount: 42, andSpindleCount: 14)","let ref = Link(to: destination)"]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"함수나 메소드 이름은 부작용(side-effects) 여부에 따라 다르게 정한다","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"부작용이 없는 경우는 명사형으로 작성한다. 예시) x.distance(to: y), i.successor()."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"부작용이 있는 경우에는 명령형으로 동사로 작성한다. 예시) print(x), x.sort(), x.append(y).","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"가변\/불변 메소드 이름을 함께 고려하라."}]}]}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"text":"동사로 표현하면 자연스럽게","type":"text"}]},{"type":"text","text":" “ed”나 “ing”를 붙여서 불변 메소드 이름을 만들 수 있다."}],"type":"paragraph"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"가변(Mutating) x.sort(), x.append(y)"}],"type":"paragraph"}],"style":"note","name":"Note","type":"aside"},{"name":"Note","style":"note","type":"aside","content":[{"inlineContent":[{"type":"text","text":"불변(Nonmutating) z = x.sorted(), z = x.appending(y)"}],"type":"paragraph"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"(“ed”를 붙여서) 동사 과거형으로 불변성을 작성하기 적합한 경우"}]}]}]},{"code":["\/\/\/ Reverses `self` in-place.","mutating func reverse()","\/\/\/ Returns a reversed copy of `self`.","func reversed() -> Self","...","x.reverse()","let y = x.reversed()"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"목적어가 있어서 문법적으로 “ed”를 붙이기 어렵고, “ing”가 적합한 경우"}]}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["\/\/ Strips all the newlines from `self`","mutating func stripNewlines()","\/\/\/ Returns a copy of `self` with all the newlines stripped.","func strippingNewlines() -> String","...","s.stripNewlines()","let oneLine = t.strippingNewlines()"]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"동작을 명사로 표현하기 적합한 경우에는 가변 메소드 이름에 “form-“을 머릿말로 붙인다."}]}]}]},{"type":"aside","name":"Note","style":"note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"불변 x = y.union(z), j = c.successor(i)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"가변 y.formUnion(z), c.formSuccessor(&i)"}]}],"type":"aside","style":"note","name":"Note"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"불변으로 사용할 때 ","type":"text"},{"inlineContent":[{"type":"text","text":"부울린 메소드나 프로퍼티를 사용할 때는 리턴값을 받아서 단언 구문(Assertion)처럼 읽도록 한다."}],"type":"strong"},{"text":" 예시) x.isEmpty, line1.intersects(line2).","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"어떤 것을 표현하는 프로토콜은 명사처럼 읽도록 명시한다."}]},{"type":"text","text":" 예시) Collection"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"기능이나 가능성을 표현하는 프로토콜은 -able, -ible, -ing 등을 붙여서 표현한다."}]},{"text":" 예시) Equatable, ProgressReporting.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"그 외에 ","type":"text"},{"inlineContent":[{"type":"text","text":"상수, 변수, 속성, 타입들은 명사로 읽도록 명시한다."}],"type":"strong"}],"type":"paragraph"}]}]},{"type":"heading","level":2,"text":"용어를 잘 표현하라 (Use Terminology Well)","anchor":"%EC%9A%A9%EC%96%B4%EB%A5%BC-%EC%9E%98-%ED%91%9C%ED%98%84%ED%95%98%EB%9D%BC-Use-Terminology-Well"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"inlineContent":[{"type":"text","text":"애매한 용어를 피하라."}],"type":"strong"},{"type":"text","text":" ’피부(skin)’를 ’표피(epidermis)’라고 하지말고, 더 쉽게 의미를 전달할 수 있는 표현이 있으면 그걸 선택하라. 전문 용어(Term of Art)는 필수적인 소통 도구지만, 사용하지 않을 경우 놓칠 수 있는 중요한 의미를 꼭 표현해야 하는 경우만 사용하세요."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"전문 용어를 사용한다면 ","type":"text"},{"inlineContent":[{"type":"text","text":"기존 의미에 맞춰 사용하라."}],"type":"strong"},{"type":"text","text":" 일반적인 용어가 애매하거나 불명확한 것을 정확하게 표현하기 위해서만 기술적인 용어를 사용하는 것이 좋다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"전문가를 놀라게 하지마라: 기존에 친숙하게 사용하던 용어에 전혀 새로운 의미를 부여한다면 선배들이 놀라거나 화를 낼지도 모른다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"초보자를 헷갈리게 하지마라: 웹에서 용어를 찾아 공부하는 사람들에게도 용어의 전형적인 의미가 중요하다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"축약어를 피하라."}]},{"type":"text","text":" 전문 용어면서 표준 형태가 아닌 약자는 잘못 풀어쓰거나 오해를 할 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"text":"선례를 받아드려라.","type":"text"}]},{"type":"text","text":" 기존 문화에 맞춰진 표현은 왕초보를 위해서 줄이지마라."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"연속된 데이터 구조 이름은 단순히 초보자가 이해하기에 List가 더 쉬울더라도 Array를 사용하는 게 좋다. 배열(Array)은 프로그래밍을 공부하는 모든 사람들이 공부하는 일반적인 용어라서, 검색하거나 질문을 할 때도 더 적합한 이름이라고 할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"수학처럼 전문 영역에 대한 용어도 verticalPositionOnUnitCircleAtOriginOfEndOfRadiusWithAngle(x)보다 sin(x)처럼 폭넓게 사용하는 기존 선계를 지켜야한다. 비록 sine이 완전한 단어 표현이지만, 개발자나 수학자들은 sin(x)가 더 친숙하기 때문이다."}]}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"anchor":"%EC%A0%95%EB%A6%AC","type":"heading","level":2,"text":"정리"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"코드를 읽을 때, 코드를 읽는 것처럼 느끼도록 하면 안된다. 문서를 읽는 것 처럼 읽혀야 한다.","type":"text"}],"type":"strong"}]},{"inlineContent":[{"type":"text","text":"변수는 명사, 함수는 동사"}],"type":"paragraph"},{"inlineContent":[{"text":"변수는 값을 담는 공간, 함수는 그 값들로 일을 한다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"변수와 함수는 lowerCamelCase 클래스명은 UpperCamelCase"}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"text","text":"lowerCamelCase : fuction, method, variable, constant"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"ex.) someVariableName"}]},{"inlineContent":[{"text":"UpperCamelCase : type(class, struct, enum, extension…)","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"ex.) Person, Point, Week"}]},{"text":"공식문서 참고","level":2,"type":"heading","anchor":"%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C-%EC%B0%B8%EA%B3%A0"},{"inlineContent":[{"identifier":"https:\/\/minsone.github.io\/swift-internals\/api-design-guidelines\/","isActive":true,"type":"reference"}],"type":"paragraph"}]}],"variants":[{"paths":["\/documentation\/arinote\/210805_api_design_guidelines"],"traits":[{"interfaceLanguage":"swift"}]}],"sections":[],"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/210805_API_Design_Guidelines"},"schemaVersion":{"minor":3,"major":0,"patch":0},"metadata":{"title":"210805 API Design Guidelines","role":"article","roleHeading":"Article","modules":[{"name":"Ari's Notes"}]},"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://minsone.github.io/swift-internals/api-design-guidelines/":{"title":"https:\/\/minsone.github.io\/swift-internals\/api-design-guidelines\/","identifier":"https:\/\/minsone.github.io\/swift-internals\/api-design-guidelines\/","type":"link","titleInlineContent":[{"text":"https:\/\/minsone.github.io\/swift-internals\/api-design-guidelines\/","type":"text"}],"url":"https:\/\/minsone.github.io\/swift-internals\/api-design-guidelines\/"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/210806_----_--_ios_---.json b/docs/data/documentation/arinote/210806_----_--_ios_---.json new file mode 100644 index 0000000..8b75872 --- /dev/null +++ b/docs/data/documentation/arinote/210806_----_--_ios_---.json @@ -0,0 +1 @@ +{"schemaVersion":{"minor":3,"major":0,"patch":0},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"sections":[],"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"210806 왕초보를 위한 IOS 앱개발"},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/210806_----_--_IOS_---"},"primaryContentSections":[{"kind":"content","content":[{"type":"heading","anchor":"TIL-Today-I-Learned","level":1,"text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"type":"text","text":"날짜: 2021년 8월 6일"},{"text":" ","type":"text"},{"type":"text","text":"작성자: 이아리"},{"type":"text","text":" "},{"text":"태그: Simulator, Xcode, iOS, macOS, 야곰닷넷","type":"text"}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/user-attachments\/assets\/4b8dea78-167c-4b0b-b470-01af4f9646ed","type":"image"}]},{"type":"heading","anchor":"%ED%95%99%EC%8A%B5%EB%82%B4%EC%9A%A9","text":"학습내용","level":2},{"inlineContent":[{"type":"text","text":"오늘은 평소 공부하고 있는 Swift 문법 말고도 추가로 어떤걸 공부하면 좋을지 혼자 고민하다가 질문방에 들어가 고민을 털어놓았고 오동나무님의 추천으로 야곰닷넷에 ‘왕초보를 위한 iOS 앱개발’ 라는 강의를 들어보기로 결정하였다. Swift 문법에 대해 탐구하다가 처음으로 iOS에 접근하게 되었는데 단순히 따라하는 것만으로도 재밌고 흥미로웠다. 그러나 아직 Xcode UI가 익숙치 않아 어떤 기능을 하는지, 단축키는 뭐가있는지 더 알아봐야할 것 같다. 그렇게 순조롭게 강의를 따라하던 와중에 나에게 큰 시련이 닥쳐왔다…."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/ec1686c0-aa85-4976-98ba-485f7f7d8d67"}]},{"level":2,"type":"heading","text":"문제점","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90"},{"type":"paragraph","inlineContent":[{"type":"text","text":"나는 현재 "},{"type":"strong","inlineContent":[{"text":"실리콘 맥북에어","type":"text"}]},{"type":"text","text":"를 사용하고 있다. 그리고 도전정신이 강한 나머지 현재 "},{"type":"strong","inlineContent":[{"type":"text","text":"macOS 버전은 Monterey"}]},{"text":"다. 또 Xcode 12는 몬터레이로 업그레이드 하니 알수없는 오류로 자주 강제종료 되어 ","type":"text"},{"inlineContent":[{"type":"text","text":"Xcode beta (Xcode 13)"}],"type":"strong"},{"type":"text","text":" 버전을 사용하고 있었다. (강제종료의 에러는 해결되지 않았다.) 그래서 강의를 따라하던 도중 ‘image literal’을 추가하는 과정에서 버전이 달라 제대로 추가가 되지 않았다. 처음엔 Xcode 13에서 image literal을 추가하는 방법을 구글링 하여 뒤져보았으나, 베타버전은 아무도 사용하지 않는지 해결 방안이 좀처럼 나오지가 않았다. 그래서 평소에 켜지 않았던 Xcode 12를 다시 실행시켰다. 그리고 13에서 진행했던 프로젝트를 열었으나 프로젝트 버전 호환문제로 계속 강제종료가 되는 것이였다. 그래서 그냥 복습하자는 의미로 Xcode 12에서 강의를 보며 따라했던 것을 다시 처음부터 후다닥 만들어냈다. 만들고 나서 시뮬레이터를 실행하니 이번엔 ‘Could not attach to pid’ 에러로 정상적인 실행이 되지가 않았다. 이것 또한 macOS를 Monterey로 업데이트해서 생기는 문제인 것인가…."}]},{"level":2,"anchor":"%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","type":"heading","text":"해결 방법"},{"type":"paragraph","inlineContent":[{"text":"시뮬레이터 문제를 해결하려고 구글링 해보았으나 나와 같은 에러가 뜨는 사람은 없는 것 같았다. 나의 예상으로는 macOS와 Xcode의 호환문제인 것 같았다. 베타버전이 아닌 정식버전으로 해결해보려고 했으나 또다른 문제가 발생해서 Xcode 베타버전으로 해결할 수 있는 방법이 없을까 하고 찾아보다가 새로운 베타버전이 나왔다는 사실을 알게되어 바로 새 버전으로 다운로드 해서 문제를 해결했다. 릴리즈를 확인해보니 literal 관련한 오류가 해결되었다는 내용도 확인되었다. 앞으로 개발을 하려면 OS 업데이트에 과감한 도전을 하기 보다는 좀 더 개발 환경의 안전성을 위해 신경 써야할 것 같다….","type":"text"}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/user-attachments\/assets\/b8afc4b3-0b22-4a5f-b0e6-9fd19da524b7","type":"image"}]},{"type":"heading","anchor":"%EB%B0%B0%EC%9A%B4-%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC","text":"배운 내용 정리","level":2},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[To-Do List]","type":"text"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Add Slider ✔️"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Add ‘HIT Button’ ✔️"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Receive value changed events from the slider ✔️"}]}]},{"content":[{"inlineContent":[{"text":"add ‘RESET Button’ ✔️","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Add labels pressenting information"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Generate the random number"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Compare the random number with input number","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Show alerts"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Implement ‘reset’ feature"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Add ‘Credit’ view"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"text":"라이브러리 단축키 : CMD + Shift + L","type":"text"}]},{"inlineContent":[{"type":"text","text":"빌드 단축키: CMD + R"}],"type":"paragraph"},{"type":"heading","level":3,"anchor":"Asset","text":"Asset"},{"type":"paragraph","inlineContent":[{"text":"Xcode에서 앱에 사용될 리소스(이미지, 음악 파일 등)의 집합을 나타내는 요소.","type":"text"}]},{"text":"UIkit 이란?","type":"heading","anchor":"UIkit-%EC%9D%B4%EB%9E%80","level":3},{"inlineContent":[{"text":"사용자 인터페이스와 사용자 경험을 위한 대부분의 구현체를 포함하는 프레임워크","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"사용자 인터페이스","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":"View and Control : 화면에 콘텐츠 표시"}]},{"inlineContent":[{"text":"View Controller : 사용자 인터페이스 관리","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"Animation and Haptics : 애니메이션과 햅틱을 통한 피드백 제공","type":"text"}]},{"type":"paragraph","inlineContent":[{"text":"Window and Screen : 뷰 계층을 위한 윈도우 제공","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"사용자 액션"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"text":"Touch, Press, Gesture: 제스처 인식기를 통한 이벤트 처리 로직","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"Drag and Drop: 화면 위에서 드래그 앤 드롭 기능"}]},{"inlineContent":[{"type":"text","text":"Peek and Pop: 3D 터치에 대응한 미리 보기 기능"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"Keyboard and Menu: 키보드 입력을 처리 및 사용자 정의 메뉴 표시"}],"type":"paragraph"},{"type":"heading","anchor":"IBOutlet","text":"@IBOutlet","level":3},{"type":"paragraph","inlineContent":[{"type":"text","text":"클래스의 프로퍼티와 인터페이스 빌터의 요소를 연결하기 위해 프로퍼티 앞에 붙이는 어노테이션"}]},{"level":3,"anchor":"IBAction","type":"heading","text":"@IBAction"},{"type":"paragraph","inlineContent":[{"type":"text","text":"클래스의 액션 메서드와 인터페이스 빌더의 이벤트를 연결하기 위해 메서드 앞에 붙이는 어노테이션"}]},{"text":"Event Driven Programming","anchor":"Event-Driven-Programming","level":3,"type":"heading"},{"inlineContent":[{"text":"여러 이벤트에 의해 실행할 코드가 결정되는데, 이것을 ‘이벤트 기반 프로그래밍’ 혹은 ‘이벤트 주도 프로그래밍’이라고 표현한다.","type":"text"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"사용자 혹은 시스템 등에서 여러 상황에 전달한다. 사용자의 이벤트가 없는 경우라면 코드를 실행되지 않고 무한정 기다리게 된다."}],"type":"paragraph"},{"type":"heading","text":"Label","anchor":"Label","level":3},{"type":"paragraph","inlineContent":[{"type":"text","text":"텍스트를 보여주는 오브젝트"}]},{"anchor":"Asset-Catalog","level":3,"type":"heading","text":"Asset Catalog"},{"type":"paragraph","inlineContent":[{"type":"text","text":"어플리케이션에 사용될 에셋들을 모아놓은 카탈로그"}]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"Asset Types"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"App Icon","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Color set","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Data set (Sounds, Docs, Videos…)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Image set"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Launch image"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Sticker"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Watch complication"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"…"}],"type":"paragraph"}]}]},{"type":"heading","anchor":"Auto-Layout","text":"Auto Layout","level":3},{"inlineContent":[{"type":"text","text":"뷰에 배치된 제약조건을 기반으로 뷰 계층에 있는 모든 뷰의 크기와 위치를 동적으로 계산하여 배치하는 Apple의 레이아웃 엔진. 한번의 설정으로 서로 다른 환경의 화면에서 자연스러운 배치가 가능하게 해주고 가로모드와 세로모드 등 화면의 변환이 이루어져도 유동적으로 배치를 해준다. 제약시 하나의 오브젝트에 여러번 설정하면 충돌이 일어날 수 있으니 주의해야 한다."}],"type":"paragraph"}]}],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/210806_----_--_ios_---"]}],"kind":"article","references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://github.com/user-attachments/assets/4b8dea78-167c-4b0b-b470-01af4f9646ed":{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/4b8dea78-167c-4b0b-b470-01af4f9646ed","alt":"4","variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/4b8dea78-167c-4b0b-b470-01af4f9646ed","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://github.com/user-attachments/assets/b8afc4b3-0b22-4a5f-b0e6-9fd19da524b7":{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/b8afc4b3-0b22-4a5f-b0e6-9fd19da524b7","alt":"6","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/b8afc4b3-0b22-4a5f-b0e6-9fd19da524b7"}]},"https://github.com/user-attachments/assets/ec1686c0-aa85-4976-98ba-485f7f7d8d67":{"type":"image","variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/ec1686c0-aa85-4976-98ba-485f7f7d8d67","traits":["1x","light"]}],"alt":"5","identifier":"https:\/\/github.com\/user-attachments\/assets\/ec1686c0-aa85-4976-98ba-485f7f7d8d67"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/210807_----_--_ios_---.json b/docs/data/documentation/arinote/210807_----_--_ios_---.json new file mode 100644 index 0000000..9736814 --- /dev/null +++ b/docs/data/documentation/arinote/210807_----_--_ios_---.json @@ -0,0 +1 @@ +{"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/210807_----_--_IOS_---"},"schemaVersion":{"minor":3,"major":0,"patch":0},"kind":"article","sections":[],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"primaryContentSections":[{"content":[{"type":"heading","anchor":"TIL-Today-I-Learned","level":1,"text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"type":"text","text":"날짜: 2021년 8월 7일"},{"type":"text","text":" "},{"text":"작성자: 이아리","type":"text"},{"text":" ","type":"text"},{"text":"태그: Alerts, Bulid, Simulator, Swift, Xcode, iOS, 야곰닷넷","type":"text"}]},{"text":"학습내용","level":2,"anchor":"%ED%95%99%EC%8A%B5%EB%82%B4%EC%9A%A9","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"오늘은 어제 진행하였던 ‘야곰의 왕초보를 위한 iOS 앱개발’ 강의를 끝내기 위해 공부하였다. 라이브러리 추가, 오토 레이아웃 설정 등 앱개발을 위한 "},{"type":"strong","inlineContent":[{"type":"text","text":"기본적인 기능"}]},{"type":"text","text":"을 배웠고, 중간중간 "},{"inlineContent":[{"type":"text","text":"Swift 문법"}],"type":"strong"},{"text":"에 대한 내용도 학습했다. 완성된 프로젝트를 내 기기에 설치하여 직접 실행해보기도 하였다. 아직은 떠오르는 게 없지만 훗날 내가 만들고 싶은 앱이 생기면 꼭 한번 도전해보고 싶다! 앞으로 앱 개발을 하기 위한 키워드들도 잔뜩 얻어가는 느낌이라 뿌듯했다. 어제처럼 Xcode나 Simulator에 특별한 에러사항 없이 무탈하게 강의를 끝내서 참 다행이다.","type":"text"}]},{"anchor":"To-Do-List","level":2,"text":"To-Do List","type":"heading"},{"items":[{"content":[{"inlineContent":[{"text":"Add Slider ✔️","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Add ‘HIT Button’ ✔️"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Receive value changed events from the slider ✔️","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"add ‘RESET Button’ ✔️","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Add labels pressenting information ✔️","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Generate the random number ✔️","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Compare the random number with input number ✔️","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Show alerts ✔️"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Implement ‘reset’ feature ✔️","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Add ‘Credit’ view ✔️","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"학습 내용 정리"}]},{"level":2,"text":"주석","type":"heading","anchor":"%EC%A3%BC%EC%84%9D"},{"inlineContent":[{"type":"text","text":"프로그래밍을 하면서 코드에 대한 내용을 메모하는 목적으로 사용된다. 소스코드를 더 쉽게 이해할 수 있도록 도와주는 텍스트. 주석을 남용하면 코드가 지저분해질 수 있기 때문에 주의해서 사용하는 것이 좋다."}],"type":"paragraph"},{"text":"함수","anchor":"%ED%95%A8%EC%88%98","type":"heading","level":2},{"inlineContent":[{"text":"func 함수이름(파라미터) { … }","type":"text"}],"type":"paragraph"},{"inlineContent":[{"text":"특정한 기능을 하는 코드의 모음, 코드조각이라고 보면 된다.","type":"text"}],"type":"paragraph"},{"style":"note","name":"변수 ","type":"aside","content":[{"inlineContent":[{"type":"text","text":"임시적으로 값을 저장하는 저장소중에 하나 (var)"}],"type":"paragraph"}]},{"type":"aside","style":"note","name":"상수","content":[{"inlineContent":[{"type":"text","text":"값을 저장한 후 변경할 수가 없음. (let)"}],"type":"paragraph"}]},{"anchor":"%EB%B2%94%EC%9C%84%EC%97%B0%EC%82%B0%EC%9E%90","level":2,"text":"범위연산자","type":"heading"},{"inlineContent":[{"text":"1…5 : 폐쇄범위연산자","type":"text"}],"type":"paragraph"},{"inlineContent":[{"text":"1…<5 : 반폐쇄범위연산자","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"1… \/ …5 \/ ..<5 : 단방향범위연산자"}]},{"level":2,"text":"비교연산자","type":"heading","anchor":"%EB%B9%84%EA%B5%90%EC%97%B0%EC%82%B0%EC%9E%90"},{"inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/bb5992a1-e249-4869-b179-94776c1fc9de"}],"type":"paragraph"},{"level":2,"type":"heading","text":"문자열 보간법","anchor":"%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%B3%B4%EA%B0%84%EB%B2%95"},{"inlineContent":[{"type":"text","text":"문자를 다른값과 합성을 한다. 혹은 문자에 포함된 변수의 실제 값을 치환하여 넣는 것"}],"type":"paragraph"},{"inlineContent":[{"identifier":"https:\/\/github.com\/user-attachments\/assets\/e113d97f-ba19-4e9c-9244-f2210525b319","type":"image"}],"type":"paragraph"},{"anchor":"return%EB%AC%B8","type":"heading","level":2,"text":"return문"},{"type":"paragraph","inlineContent":[{"type":"text","text":"return문을 넣으면 함수를 바로 끝낼 수 있다. (아래 return문 다음 아래에 있는 if문은 실행되지 않음)"}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/9645d991-f132-4b45-9952-5f7879b5c379"}],"type":"paragraph"},{"type":"heading","anchor":"%EC%A1%B0%EA%B1%B4%EB%AC%B8","text":"조건문","level":2},{"inlineContent":[{"text":"if문에는 else if로 또다른 조건을 추가할 수 있다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/user-attachments\/assets\/fabb549f-6e46-411a-90a4-dfde603dcd3c","type":"image"}]},{"type":"heading","level":2,"text":"초기화","anchor":"%EC%B4%88%EA%B8%B0%ED%99%94"},{"type":"paragraph","inlineContent":[{"type":"text","text":"변수(var)는 새로 초기화하여 변경이 가능하다!"}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/74e2abc4-6ea0-437a-8801-b1d6f5c71761"}],"type":"paragraph"},{"anchor":"%EC%95%8C%EB%A6%BC%EC%B0%BD-Alert-%EB%A7%8C%EB%93%A4%EA%B8%B0","type":"heading","level":2,"text":"알림창 Alert 만들기"},{"inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/1d51a696-6325-4462-a8aa-3b05fdd3ac27"}],"type":"paragraph"},{"type":"heading","text":"또 다른 ViewController 생성하는 방법.","level":2,"anchor":"%EB%98%90-%EB%8B%A4%EB%A5%B8-ViewController-%EC%83%9D%EC%84%B1%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/c5c0ef35-cac9-4ac1-948a-713c8635a0c2"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/aedb68d7-62ff-453c-826b-ff7e8138de84"}]},{"inlineContent":[{"type":"text","text":"File > File… > Cocoa Touch Class 로 진입"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/user-attachments\/assets\/910901b4-1cfc-496d-80ee-76e818167c14","type":"image"}]},{"inlineContent":[{"type":"text","text":"Subclass of를 UIViewController로 변경해주고 Class 이름을 임의로 수정해준다."}],"type":"paragraph"},{"inlineContent":[{"identifier":"https:\/\/github.com\/user-attachments\/assets\/f8d53929-5f10-47d2-8570-314fc748c550","type":"image"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/user-attachments\/assets\/e662a18f-1cb1-4eb8-ada4-a3b5953c0719","type":"image"}]},{"inlineContent":[{"type":"text","text":"라이브러리에서 View Controller를 추가한다."}],"type":"paragraph"},{"type":"heading","anchor":"Apple-%EB%94%94%EC%9E%90%EC%9D%B8-%EA%B0%80%EC%9D%B4%EB%93%9C-%EA%B3%B5%EC%8B%9D%EB%AC%B8%EC%84%9C","level":2,"text":"Apple 디자인 가이드 공식문서"},{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/overview\/themes\/","isActive":true}]},{"anchor":"Button--Text-View--Image-View-%EC%B6%94%EA%B0%80%ED%9B%84-Auto-Layout-%EC%84%A4%EC%A0%95","type":"heading","text":"Button \/ Text View \/ Image View 추가후 Auto Layout 설정","level":2},{"type":"paragraph","inlineContent":[{"text":"이제까지 배웠던 내용 다시 복습하는 느낌으로 새로운 info화면 구성해주기.","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/731c471f-ee5d-4fc2-a602-c03696c8fd48"}]},{"type":"heading","text":"info 버튼 연결하기 (Storyboard Segue이용하여 화면 전환)","level":2,"anchor":"info-%EB%B2%84%ED%8A%BC-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0-Storyboard-Segue%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%ED%99%94%EB%A9%B4-%EC%A0%84%ED%99%98"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/user-attachments\/assets\/7f6c2b8f-9020-4033-892d-dca8ba680604","type":"image"}]},{"inlineContent":[{"text":"Ctrl 누른 상태에서 Button을 전환할 화면으로 끌어당겨서 놓아주기.","type":"text"}],"type":"paragraph"},{"inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/25915ffe-a5ea-484f-949a-146758a1d009"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Present Modally 클릭"}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/user-attachments\/assets\/2daf053b-0430-4184-9d2e-31e5735fce27","type":"image"}]},{"type":"paragraph","inlineContent":[{"text":"Transition을 변경해보자. (전환 효과)","type":"text"}]},{"level":2,"text":"닫기버튼 구현하기 위해 함수 만들기. dismiss()","anchor":"%EB%8B%AB%EA%B8%B0%EB%B2%84%ED%8A%BC-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%B4-%ED%95%A8%EC%88%98-%EB%A7%8C%EB%93%A4%EA%B8%B0-dismiss","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/0b69ca5d-142f-4c33-88fb-d91a88bb08be"}]},{"type":"heading","text":"닫기를 구현한 함수, 버튼 액션 연결해주기","level":2,"anchor":"%EB%8B%AB%EA%B8%B0%EB%A5%BC-%EA%B5%AC%ED%98%84%ED%95%9C-%ED%95%A8%EC%88%98-%EB%B2%84%ED%8A%BC-%EC%95%A1%EC%85%98-%EC%97%B0%EA%B2%B0%ED%95%B4%EC%A3%BC%EA%B8%B0"},{"inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/7c149d8d-b71a-49dd-8770-05319e048f73"}],"type":"paragraph"},{"text":"Xcode의 환경설정 단축키","level":2,"type":"heading","anchor":"Xcode%EC%9D%98-%ED%99%98%EA%B2%BD%EC%84%A4%EC%A0%95-%EB%8B%A8%EC%B6%95%ED%82%A4"},{"inlineContent":[{"text":"CMD + ,","type":"text"}],"type":"paragraph"},{"level":2,"anchor":"%EB%82%B4-%EA%B8%B0%EA%B8%B0%EC%97%90%EC%84%9C-%EC%95%B1-%EC%8B%A4%ED%96%89%ED%95%98%EA%B8%B0%EC%A0%84%EC%97%90-Xcode-%EC%84%A4%EC%A0%95%EC%97%90%EC%84%9C-%EB%82%98%EC%9D%98-Apple-ID-%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0","type":"heading","text":"내 기기에서 앱 실행하기전에 Xcode 설정에서 나의 Apple ID 추가하기"},{"inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/19986415-ad1b-4121-bb45-8947b564c965"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"Xcode 환경설정에 들어가서 Accounts 탭 클릭"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"나의 Apple ID를 등록해준다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/user-attachments\/assets\/fa175fa5-2d5e-4022-b50b-063323d4b7bd","type":"image"}]},{"type":"paragraph","inlineContent":[{"text":"UpDownGame 프로젝트 설정 > Signing & Capabilities으로 진입 후 Team을 등록한 Apple ID로 설정해준다.","type":"text"}]},{"level":2,"text":"내 기기에서 직접 실행해보기","anchor":"%EB%82%B4-%EA%B8%B0%EA%B8%B0%EC%97%90%EC%84%9C-%EC%A7%81%EC%A0%91-%EC%8B%A4%ED%96%89%ED%95%B4%EB%B3%B4%EA%B8%B0","type":"heading"},{"inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/5b68c291-3a93-4b91-8249-da9ab2d8c216"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"Window > Devices and Simulators","type":"text"}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/user-attachments\/assets\/5da31efc-65f6-4c42-9d22-615fb43c618b","type":"image"}]},{"inlineContent":[{"identifier":"https:\/\/github.com\/user-attachments\/assets\/7e8224f7-74f8-413e-8e48-9d729f6a71cd","type":"image"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"기기와 컴퓨터 케이블로 연결하면 아무것도 없던 Devices에서 내 폰이 뜬다."}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"기기에서 컴퓨터와 신뢰하겠다는 버튼 클릭."}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"설정 > 일반 > VPN 및 기기 관리 로 들어가서 개발자 앱의 권한을 승인해준다."}]},{"inlineContent":[{"text":"정상적으로 실행되는지 확인해본다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/ee804904-9740-42f2-b8c5-26ebcdad87f1"}]}]}],"type":"unorderedList"}],"kind":"content"}],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/210807_----_--_ios_---"]}],"metadata":{"title":"210807 왕초보를 위한 IOS 앱개발","role":"article","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article"},"references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://github.com/user-attachments/assets/2daf053b-0430-4184-9d2e-31e5735fce27":{"alt":"ex_screenshot","identifier":"https:\/\/github.com\/user-attachments\/assets\/2daf053b-0430-4184-9d2e-31e5735fce27","type":"image","variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/2daf053b-0430-4184-9d2e-31e5735fce27","traits":["1x","light"]}]},"https://github.com/user-attachments/assets/fa175fa5-2d5e-4022-b50b-063323d4b7bd":{"variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/fa175fa5-2d5e-4022-b50b-063323d4b7bd","traits":["1x","light"]}],"type":"image","alt":"ex_screenshot","identifier":"https:\/\/github.com\/user-attachments\/assets\/fa175fa5-2d5e-4022-b50b-063323d4b7bd"},"https://github.com/user-attachments/assets/5da31efc-65f6-4c42-9d22-615fb43c618b":{"variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/5da31efc-65f6-4c42-9d22-615fb43c618b"}],"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/5da31efc-65f6-4c42-9d22-615fb43c618b","alt":"ex_screenshot"},"https://github.com/user-attachments/assets/7f6c2b8f-9020-4033-892d-dca8ba680604":{"alt":"ex_screenshot","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/7f6c2b8f-9020-4033-892d-dca8ba680604"}],"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/7f6c2b8f-9020-4033-892d-dca8ba680604"},"https://github.com/user-attachments/assets/5b68c291-3a93-4b91-8249-da9ab2d8c216":{"alt":"ex_screenshot","variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/5b68c291-3a93-4b91-8249-da9ab2d8c216","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/5b68c291-3a93-4b91-8249-da9ab2d8c216"},"https://github.com/user-attachments/assets/ee804904-9740-42f2-b8c5-26ebcdad87f1":{"alt":"ex_screenshot","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/ee804904-9740-42f2-b8c5-26ebcdad87f1"}],"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/ee804904-9740-42f2-b8c5-26ebcdad87f1"},"https://github.com/user-attachments/assets/fabb549f-6e46-411a-90a4-dfde603dcd3c":{"alt":"ex_screenshot","variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/fabb549f-6e46-411a-90a4-dfde603dcd3c","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/fabb549f-6e46-411a-90a4-dfde603dcd3c"},"https://github.com/user-attachments/assets/bb5992a1-e249-4869-b179-94776c1fc9de":{"alt":"ex_screenshot","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/bb5992a1-e249-4869-b179-94776c1fc9de"}],"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/bb5992a1-e249-4869-b179-94776c1fc9de"},"https://github.com/user-attachments/assets/1d51a696-6325-4462-a8aa-3b05fdd3ac27":{"alt":"ex_screenshot","variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/1d51a696-6325-4462-a8aa-3b05fdd3ac27","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/1d51a696-6325-4462-a8aa-3b05fdd3ac27"},"https://github.com/user-attachments/assets/f8d53929-5f10-47d2-8570-314fc748c550":{"alt":"ex_screenshot","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/f8d53929-5f10-47d2-8570-314fc748c550"}],"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/f8d53929-5f10-47d2-8570-314fc748c550"},"https://github.com/user-attachments/assets/74e2abc4-6ea0-437a-8801-b1d6f5c71761":{"alt":"ex_screenshot","variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/74e2abc4-6ea0-437a-8801-b1d6f5c71761","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/74e2abc4-6ea0-437a-8801-b1d6f5c71761"},"https://developer.apple.com/design/human-interface-guidelines/ios/overview/themes/":{"titleInlineContent":[{"text":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/overview\/themes\/","type":"text"}],"url":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/overview\/themes\/","title":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/overview\/themes\/","type":"link","identifier":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/overview\/themes\/"},"https://github.com/user-attachments/assets/0b69ca5d-142f-4c33-88fb-d91a88bb08be":{"alt":"ex_screenshot","variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/0b69ca5d-142f-4c33-88fb-d91a88bb08be","traits":["1x","light"]}],"identifier":"https:\/\/github.com\/user-attachments\/assets\/0b69ca5d-142f-4c33-88fb-d91a88bb08be","type":"image"},"https://github.com/user-attachments/assets/19986415-ad1b-4121-bb45-8947b564c965":{"alt":"ex_screenshot","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/19986415-ad1b-4121-bb45-8947b564c965"}],"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/19986415-ad1b-4121-bb45-8947b564c965"},"https://github.com/user-attachments/assets/e662a18f-1cb1-4eb8-ada4-a3b5953c0719":{"identifier":"https:\/\/github.com\/user-attachments\/assets\/e662a18f-1cb1-4eb8-ada4-a3b5953c0719","alt":"ex_screenshot","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/e662a18f-1cb1-4eb8-ada4-a3b5953c0719"}],"type":"image"},"https://github.com/user-attachments/assets/7e8224f7-74f8-413e-8e48-9d729f6a71cd":{"identifier":"https:\/\/github.com\/user-attachments\/assets\/7e8224f7-74f8-413e-8e48-9d729f6a71cd","alt":"ex_screenshot","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/7e8224f7-74f8-413e-8e48-9d729f6a71cd"}],"type":"image"},"https://github.com/user-attachments/assets/aedb68d7-62ff-453c-826b-ff7e8138de84":{"identifier":"https:\/\/github.com\/user-attachments\/assets\/aedb68d7-62ff-453c-826b-ff7e8138de84","alt":"ex_screenshot","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/aedb68d7-62ff-453c-826b-ff7e8138de84"}],"type":"image"},"https://github.com/user-attachments/assets/7c149d8d-b71a-49dd-8770-05319e048f73":{"identifier":"https:\/\/github.com\/user-attachments\/assets\/7c149d8d-b71a-49dd-8770-05319e048f73","alt":"ex_screenshot","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/7c149d8d-b71a-49dd-8770-05319e048f73"}],"type":"image"},"https://github.com/user-attachments/assets/9645d991-f132-4b45-9952-5f7879b5c379":{"identifier":"https:\/\/github.com\/user-attachments\/assets\/9645d991-f132-4b45-9952-5f7879b5c379","alt":"ex_screenshot","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/9645d991-f132-4b45-9952-5f7879b5c379"}],"type":"image"},"https://github.com/user-attachments/assets/731c471f-ee5d-4fc2-a602-c03696c8fd48":{"identifier":"https:\/\/github.com\/user-attachments\/assets\/731c471f-ee5d-4fc2-a602-c03696c8fd48","alt":"ex_screenshot","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/731c471f-ee5d-4fc2-a602-c03696c8fd48"}],"type":"image"},"https://github.com/user-attachments/assets/e113d97f-ba19-4e9c-9244-f2210525b319":{"identifier":"https:\/\/github.com\/user-attachments\/assets\/e113d97f-ba19-4e9c-9244-f2210525b319","alt":"ex_screenshot","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/e113d97f-ba19-4e9c-9244-f2210525b319"}],"type":"image"},"https://github.com/user-attachments/assets/910901b4-1cfc-496d-80ee-76e818167c14":{"identifier":"https:\/\/github.com\/user-attachments\/assets\/910901b4-1cfc-496d-80ee-76e818167c14","alt":"ex_screenshot","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/910901b4-1cfc-496d-80ee-76e818167c14"}],"type":"image"},"https://github.com/user-attachments/assets/c5c0ef35-cac9-4ac1-948a-713c8635a0c2":{"identifier":"https:\/\/github.com\/user-attachments\/assets\/c5c0ef35-cac9-4ac1-948a-713c8635a0c2","alt":"ex_screenshot","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/c5c0ef35-cac9-4ac1-948a-713c8635a0c2"}],"type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://github.com/user-attachments/assets/25915ffe-a5ea-484f-949a-146758a1d009":{"identifier":"https:\/\/github.com\/user-attachments\/assets\/25915ffe-a5ea-484f-949a-146758a1d009","alt":"ex_screenshot","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/25915ffe-a5ea-484f-949a-146758a1d009"}],"type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/210809_enum.json b/docs/data/documentation/arinote/210809_enum.json new file mode 100644 index 0000000..f7b36dc --- /dev/null +++ b/docs/data/documentation/arinote/210809_enum.json @@ -0,0 +1 @@ +{"sections":[],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"title":"210809 enum","roleHeading":"Article"},"primaryContentSections":[{"content":[{"type":"heading","level":1,"text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"type":"text","text":"날짜: 2021년 8월 9일"},{"text":" ","type":"text"},{"text":"작성자: 이아리","type":"text"},{"type":"text","text":" "},{"type":"text","text":"태그: enum, 열거형"}]},{"text":"학습내용","anchor":"%ED%95%99%EC%8A%B5%EB%82%B4%EC%9A%A9","type":"heading","level":2},{"inlineContent":[{"type":"text","text":"오늘은 코드 스타터 캠프 2주차를 시작하는 날이다. 강의와 퀴즈를 풀어보며 평소 알고있지 못했던 enum(열거형)에 대해서 자세히 공부해보기로 하였다. 과제 또한 enum을 활용하여 작성하는 것이 가장 어려웠고, 어려웠던 만큼 열거형의 대한 개념에 대해서 잊지 않기위해 공식문서를 참고해가며 열거형의 대해서 정리해보기로 하였다."}],"type":"paragraph"},{"level":2,"text":"정리","anchor":"%EC%A0%95%EB%A6%AC","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"열거형은 Type의 일종이다. 스위프트의 열거형은 다른 언어의 열거형과는 많이 다른 특성을 갖고 있다."}]},{"type":"paragraph","inlineContent":[{"text":"주로 제한된 선택지를 주고 싶을 때, 혹은 정해진 값 외에는 입력받고 싶지 않을 때 사용한다.","type":"text"}]},{"inlineContent":[{"text":"문법은 다음과 같다.","type":"text"}],"type":"paragraph"},{"type":"codeListing","code":["enum Gender {"," case male"," case female","}"],"syntax":"swift"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"enum은 ‘타입’이므로 대문자 카멜케이스를 사용하여 이름을 정의한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"각 case는 소문자 카멜케이스로 정의한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"각 case는 그 자체가 ‘고유의 값’이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"각 case는 한 줄에 개별로도, 한 줄에 여러개도 정의할 수 있다.","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"text":"열거 값을 switch문에 일치시킬 수도 있다.","type":"text"}],"type":"paragraph"},{"code":["func checkGender(gender: Gender) {"," switch gender {"," case .male:"," print(\"남자\")"," case .female:"," print(\"여자\")"," }","}","","checkGender(gender: .male) \/\/ 남자","checkGender(gender: .female) \/\/ 여자"],"syntax":"swift","type":"codeListing"},{"type":"paragraph","inlineContent":[{"type":"text","text":"열거형은 원시 값(rawValue)을 가질 수도 있다."}]},{"syntax":"swift","code":["enum Subject: String {"," case korean = \"국어\""," case phyicalEduation = \"체육\""," case ethics = \"윤리\""," case philosophy = \"철학\"","}","","print(Subject.korean.rawValue) \/\/ \"국어\""],"type":"codeListing"},{"type":"paragraph","inlineContent":[{"type":"text","text":"열거형은 연관 값(Associated Values)도 가질 수 있다."}]},{"code":["enum Date {"," case intDate(Int, Int, Int)"," case stringDate(String)","}","","var todayDate = Date.intDate(2021, 8, 9)","todayDate = Date.stringDate(\"2021년 8월 9일\")","","switch todayDate {","case .intDate(let year, let month, let day):"," print(\"\\(year)\/\\(month)\/\\(day)\")","case .stringDate(let date):"," print(date)","}","","\/\/ 출력값 : \"2021년 8월 9일\""],"type":"codeListing","syntax":"swift"},{"inlineContent":[{"type":"text","text":"여기서 충격적 사실.. 옵셔널은 enum이다."}],"type":"paragraph"},{"type":"codeListing","code":["public enum Optional {"," case none"," case some(Wrapped)","}"],"syntax":"swift"},{"type":"paragraph","inlineContent":[{"type":"text","text":"옵셔널은 enum이기 때문에 아래와 같은 구문도 사용이 가능하다."}]},{"code":["let age: Int? = 27","","switch age {","case .none: \/\/ `nil`인 경우"," print(\"나이 정보가 없습니다.\")","case .some(let x) where x < 20:"," print(\"청소년\")","case .some(let x) where x < 65:"," print(\"성인\")","default:"," print(\"어르신\")","}","","\/\/ 출력값 : \"성인\""],"syntax":"swift","type":"codeListing"}],"kind":"content"}],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/210809_enum"]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/210809_enum","interfaceLanguage":"swift"},"kind":"article","schemaVersion":{"major":0,"minor":3,"patch":0},"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/210811_swift---_--_let---_----------.json b/docs/data/documentation/arinote/210811_swift---_--_let---_----------.json new file mode 100644 index 0000000..7432cfa --- /dev/null +++ b/docs/data/documentation/arinote/210811_swift---_--_let---_----------.json @@ -0,0 +1 @@ +{"kind":"article","primaryContentSections":[{"kind":"content","content":[{"type":"heading","anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","level":1},{"inlineContent":[{"text":"날짜: 2021년 8월 11일","type":"text"},{"type":"text","text":" "},{"type":"text","text":"작성자: 이아리"},{"type":"text","text":" "},{"text":"태그: ARC, Constants, Heap, Variables, 값타입, 메모리관리, 참조타입","type":"text"}],"type":"paragraph"},{"anchor":"%ED%95%99%EC%8A%B5%EB%82%B4%EC%9A%A9","type":"heading","text":"학습내용","level":2},{"inlineContent":[{"type":"text","text":"오늘은 캠프를 진행하다가 Set에 대한 개념이 부족하여 글쓰기를 통하여 복습하고 정리해보았다. 그리고 어제 함수를 작성하는 과정에서 왜 Swift는 var보다 let 선언을 선호하는지에 대해 궁금하여 찾아보았다. 찾다보니 Swift는 메모리 관리를 어떻게 하는지도 궁금하게 되어 찾아보았는데, 공부할 내용이 너무 방대했다. ARC를 기준으로 메모리 구조와 값타입, 참조타입에 대해서도 얕게만 알고 있었는데.. 이번 계기로 좀더 깊게 공부를 시작해봐야할 것 같다. 오늘은 시작 전에 맛보기로 공부 키워드를 습득해보았다."}],"type":"paragraph"},{"type":"heading","level":2,"anchor":"%EA%B3%B5%EB%B6%80%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC","text":"공부내용 정리"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"Swift가 var 및 let을 사용하는 것을 귀찮게 참견하는 이유?"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"상수(let)는 변경할 필요가 없는 값으로 코드를 보다 안전하고 명확하게 하기 위해 Swift는 전반적으로 상수가 사용된다. 이는 나중에 실수로 값을 변경하지 않도록 하는데 좋다. (휴먼에러)"}]}]},{"content":[{"inlineContent":[{"text":"또한 상수로 선언하면 메모리 관리에 더 좋은지 찾아보았더니, 성능에는 아무런 영향이 없다고 한다. 컴파일러가 알아서 최적화 해주기 때문이다. 따라서 Swift가 let 사용하라고 권하는 것은 이 Swift 언어가 지향하는 것이 ‘안전성’이기 때문이라 생각한다. let으로 선언하면 위와 같이 휴먼에러를 방지할 수 있으니깐 말이다.","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"관련링크","type":"text"}],"type":"strong"}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/stackoverflow.com\/questions\/53147092\/in-swift-how-bad-it-is-to-declare-variable-in-loops","type":"reference","isActive":true}]},{"inlineContent":[{"identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/TheBasics.html","isActive":true,"type":"reference"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이후 메모리의 관한 내용 찍먹.."}]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"Swift는 메모리 관리를 어떻게 할까?"}],"type":"strong"}]},{"inlineContent":[{"type":"text","text":"ARC(Automatic Reference Counting)를 사용한다."}],"type":"paragraph"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"ARC(Automatic Reference Counting)는 무엇인가?","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"메모리 영역 중 Heap 영역을 관리한다. Swift는 "},{"type":"strong","inlineContent":[{"text":"Reference Type","type":"text"}]},{"text":"들은 자동으로 Heap에 할당한다. Heap의 특징은 사용하고 난 후에는 반드시 메모리 해제를 해줘야 한다. ARC는 이러한 사용하고 남은 Heap의 메모리를 해제해준다.","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"ARC의 메모리 관리 방법"}],"type":"strong"}]},{"inlineContent":[{"type":"text","text":"Referenc Count(참조 횟수)를 계산하여 참조횟수가 0이면 더 이상 사용하지 않는 메모리라 생각하여 해제한다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"**RC(**Referenc Count)","type":"text"},{"inlineContent":[{"text":"란 무엇인가?","type":"text"}],"type":"strong"}]},{"type":"paragraph","inlineContent":[{"text":"인스턴스를 현재 누가 가르키고 있느냐 없느냐(참조하냐 안하냐)를 숫자로 표현한 것.","type":"text"}]},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"RC는 어떤 기준으로 숫자를 셀까?"}]}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Count Up : 인스턴스의 주소값을 변수에 할당할 때, 인스턴스 생성, 기존 인스턴스 다른 변수에 대입"}]},{"inlineContent":[{"type":"text","text":"Count Down : 인스턴스를 가리키던 변수가 메모리에서 해제되었을 때, nil이 지정되었을 때, 변수에 다른 값을 대입한 경우, 프로퍼티의 경우 속해 있는 클래스 인스턴스가 메모리에서 해제될 때"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"Swift에서 값 타입과 참조 타입","type":"text"}],"type":"strong"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/8d263d03-7a2b-4d33-b69d-eb7ba4be2cb4"}]}]}],"type":"unorderedList"},{"text":"오늘의 글쓰기","type":"heading","level":2,"anchor":"%EC%98%A4%EB%8A%98%EC%9D%98-%EA%B8%80%EC%93%B0%EA%B8%B0"},{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/leeari95.tistory.com\/14","isActive":true}]}]}],"sections":[],"metadata":{"roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"role":"article","title":"210811 Swift는 왜 let을 선호할까?"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"schemaVersion":{"patch":0,"major":0,"minor":3},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/210811_Swift---_--_let---_----------","interfaceLanguage":"swift"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/210811_swift---_--_let---_----------"]}],"references":{"https://leeari95.tistory.com/14":{"url":"https:\/\/leeari95.tistory.com\/14","identifier":"https:\/\/leeari95.tistory.com\/14","type":"link","title":"https:\/\/leeari95.tistory.com\/14","titleInlineContent":[{"type":"text","text":"https:\/\/leeari95.tistory.com\/14"}]},"https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html":{"url":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/TheBasics.html","identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/TheBasics.html","type":"link","title":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/TheBasics.html","titleInlineContent":[{"text":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/TheBasics.html","type":"text"}]},"https://github.com/user-attachments/assets/8d263d03-7a2b-4d33-b69d-eb7ba4be2cb4":{"identifier":"https:\/\/github.com\/user-attachments\/assets\/8d263d03-7a2b-4d33-b69d-eb7ba4be2cb4","alt":"30","type":"image","variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/8d263d03-7a2b-4d33-b69d-eb7ba4be2cb4","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://stackoverflow.com/questions/53147092/in-swift-how-bad-it-is-to-declare-variable-in-loops":{"url":"https:\/\/stackoverflow.com\/questions\/53147092\/in-swift-how-bad-it-is-to-declare-variable-in-loops","identifier":"https:\/\/stackoverflow.com\/questions\/53147092\/in-swift-how-bad-it-is-to-declare-variable-in-loops","type":"link","title":"https:\/\/stackoverflow.com\/questions\/53147092\/in-swift-how-bad-it-is-to-declare-variable-in-loops","titleInlineContent":[{"text":"https:\/\/stackoverflow.com\/questions\/53147092\/in-swift-how-bad-it-is-to-declare-variable-in-loops","type":"text"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/210812_---_--.json b/docs/data/documentation/arinote/210812_---_--.json new file mode 100644 index 0000000..a7b9051 --- /dev/null +++ b/docs/data/documentation/arinote/210812_---_--.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"kind":"content","content":[{"text":"TIL(Today I Learned)","anchor":"TILToday-I-Learned","type":"heading","level":1},{"inlineContent":[{"text":"날짜: 2021년 8월 12일","type":"text"},{"text":" ","type":"text"},{"text":"작성자: 이아리","type":"text"},{"type":"text","text":" "},{"type":"text","text":"태그: ARC, call by reference, call by value, function, inout, 메모리, 메모리관리, 메모리구조, 참조타입"}],"type":"paragraph"},{"text":"학습내용","level":2,"anchor":"%ED%95%99%EC%8A%B5%EB%82%B4%EC%9A%A9","type":"heading"},{"inlineContent":[{"text":"오늘은 앞서 ARC를 배우기 전에 메모리 구조에 대해서 배워보았다. 눈으로만 보는 것 보다는 메모장에 메모해서 정리해가며 메모리 구조에 대한 여러 글들을 읽었다. 그리고 기존에는 몰랐던 사실을 알게 되었다. 스택과 힙은 같은 영역을 사용한다는 점이였다. 그리고 그 차이점에 대해서 더 상세히 학습한 것 같다! 이후 블로그에 글로 정리하여 포스팅하였고, ARC를 배워보려고 하였으나, 예시로 클래스, 구조체 등등이 나왔다. 아직 내가 배울 단계는 아닌 것 같기도 하고.. 이해하는데 있어서 조금 어려움이 있기도 하다. 완전히 이해하려면 일단 클래스의 개념부터 공부해야 가능할 것 같았다. 왜냐하면 ARC는 힙(Heap)영역 메모리를 관리해주는 녀석인데, 힙에는 참조 타입의 값들이 들어가기 때문이다. 참조 타입에는 클래스와 클로저가 들어간다. 예시로 클래스가 나오는데 이니셜라이저와 디이니셜라이저를 예시로 가르쳐준다. 아직 해당 개념에 대해서 찍먹만 해보고… 완벽하지 않기 때문에 나중에 클래스에 대해서 제대로 배우고 난 후 ARC를 다시 파봐야할 것 같다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"그리고 야곰이 추천해준 ‘읽기 좋은 코드가 좋은 코드다’ 라는 책을 구매하여 오늘 배송받았다. 앞으로 시간날 때마다 틈틈히 읽어나가야겠다.","type":"text"}]},{"anchor":"%EC%98%A4%EB%8A%98%EC%9D%98-%EA%B8%80%EC%93%B0%EA%B8%B0","level":2,"type":"heading","text":"오늘의 글쓰기"},{"type":"paragraph","inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/leeari95.tistory.com\/15"}]},{"text":"복습내용 \/ inout이 뭔데?","anchor":"%EB%B3%B5%EC%8A%B5%EB%82%B4%EC%9A%A9--inout%EC%9D%B4-%EB%AD%94%EB%8D%B0","level":2,"type":"heading"},{"inlineContent":[{"text":"함수 inout parameter","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"Swift는 기본적으로 ‘call by value’ 형식이다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"그래서 call by reference를 구현하려면 함수의 매개변수를 inout parameter로 구현해야한다."}]},{"type":"paragraph","inlineContent":[{"text":"함수의 매개변수는 기본적으로 상수(let)이다.","type":"text"}]},{"inlineContent":[{"type":"text","text":"함수는 값을 복사하여 내부에 전달하지만, inout 키워드를 사용하면 "},{"inlineContent":[{"type":"text","text":"참조"}],"type":"strong"},{"type":"text","text":"로 전달하게 된다."}],"type":"paragraph"},{"inlineContent":[{"text":"예를 들어 일반적인 함수를 만들어서 확인해보면,","type":"text"}],"type":"paragraph"},{"syntax":"swift","code":["var A = 3","var B = 9","func swapTwoTest(_ numbersOwn: Int, _ numbersTwo: Int) {"," \/\/ numbersOwn = 6 \/\/ Cannot assign to value: 'numbersOwn' is a 'let' constant"," \/\/ numbersTwo = 18 \/\/ Cannot assign to value: 'numbersTwo' is a 'let' constant"," var C = numbersOwn"," var D = numbersTwo"," C = 6"," D = 18"," print(C, D) \/\/ 6 18","}","swapTwoTest(A, B)","print(A, B) \/\/ 3 9"],"type":"codeListing"},{"type":"paragraph","inlineContent":[{"type":"text","text":"위 주석과 같이 매개변수가 상수라서 전달받은 값을 변경하려고 하면 값을 할당할수 없다고 나온다."}]},{"type":"paragraph","inlineContent":[{"text":"또한 다른 방법으로 값을 변수로 복사하여 변경하여도 원본에는 영향을 끼치지 않는다.","type":"text"}]},{"inlineContent":[{"type":"text","text":"함수 호출이 끝난 후에도 값이 변경되지 않고 그대로인 것을 확인할 수 있다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"왜냐하면 값을 복사하여 함수에 넘겨주기 때문이다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"text":"하지만 inout 키워드를 사용하게 된다면?","type":"text"}]},{"code":["var A = 3","var B = 9","func swapTwoNumbers(_ numberOwn: inout Int,_ numberTwo: inout Int) { \/\/ 매개변수 타입 앞에 inout 키워드 추가"," let tmp = numberOwn"," numberOwn = numberTwo"," numberTwo = tmp","}","print(A, B) \/\/ 3 9","swapTwoNumbers(&A, &B) \/\/ 앰퍼샌드 '&'를 앞에 붙여줘야 한다.","print(A, B) \/\/ 9 3"],"type":"codeListing","syntax":"swift"},{"type":"paragraph","inlineContent":[{"text":"매개변수가 더이상 원본을 복사한 값이 아니라 원본 변수 A의 ","type":"text"},{"inlineContent":[{"text":"참조","type":"text"}],"type":"strong"},{"type":"text","text":"를 전달한다."}]},{"type":"paragraph","inlineContent":[{"text":"그래서 함수 내부에서 원본을 수정할 수 있는 것이다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"text":"함수가 호출이 끝나도 함수 내부에서 변경한 값이 그대로인 것을 확인할 수 있다.","type":"text"}]},{"inlineContent":[{"type":"text","text":"이것을 ‘call by reference’ 라고 한다"}],"type":"paragraph"}]}],"schemaVersion":{"minor":3,"patch":0,"major":0},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/210812_---_--"]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"metadata":{"title":"210812 메모리 구조","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article"},"kind":"article","sections":[],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/210812_---_--","interfaceLanguage":"swift"},"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://leeari95.tistory.com/15":{"type":"link","title":"https:\/\/leeari95.tistory.com\/15","identifier":"https:\/\/leeari95.tistory.com\/15","titleInlineContent":[{"type":"text","text":"https:\/\/leeari95.tistory.com\/15"}],"url":"https:\/\/leeari95.tistory.com\/15"},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/210816_----.json b/docs/data/documentation/arinote/210816_----.json new file mode 100644 index 0000000..8b3993f --- /dev/null +++ b/docs/data/documentation/arinote/210816_----.json @@ -0,0 +1 @@ +{"metadata":{"modules":[{"name":"Ari's Notes"}],"role":"article","title":"210816 접근제어","roleHeading":"Article"},"kind":"article","primaryContentSections":[{"kind":"content","content":[{"level":1,"type":"heading","text":"TIL(Today I Learned)","anchor":"TILToday-I-Learned"},{"inlineContent":[{"text":"날짜: 2021년 8월 16일","type":"text"},{"type":"text","text":" "},{"text":"작성자: 이아리","type":"text"},{"type":"text","text":" "},{"type":"text","text":"태그: Access Levels, 읽기 전용 구현, 접근수준, 접근제어"}],"type":"paragraph"},{"type":"heading","text":"학습내용","anchor":"%ED%95%99%EC%8A%B5%EB%82%B4%EC%9A%A9","level":2},{"inlineContent":[{"type":"text","text":"오늘은 3주차 첫째날이다. 목차를 보니 내가 주말동안 공부했던 내용이 대부분이였다. 복습의 의미로 천천히 진도를 나가면서 다시 한번 학습하게 되었고, 오늘은 그 중 평소 알고있었던 지식이였으나 자세히는 공부하지 못했었던 ‘접근수준’에 대해서 공부하게 되었다. 과제를 풀다보니 접근수준과 옵셔널을 이용하여 코드를 개선하라는데… 접근수준을 제어하는 것이 나는 제일 어려웠다. 어떤 인스턴스를 비공개로 설정해주고 공개로 설정해줘야 하는지 감이 잘 안잡혀서 자세히 공부를 시작하게 되었다."}],"type":"paragraph"},{"level":2,"anchor":"%EC%A0%95%EB%A6%AC","type":"heading","text":"정리"},{"type":"paragraph","inlineContent":[{"type":"text","text":"🔍 "},{"inlineContent":[{"type":"text","text":"접근제어란?"}],"type":"strong"}]},{"type":"paragraph","inlineContent":[{"text":"코드끼리 상호작용을 할 때 파일 간 또는 모듈 간에 접근을 제한할 수 있는 기능.","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"불필요한 접근으로 의도치 않은 결과를 초래하거나 꼭 필요한 부분만 제공해야 하는데 전체 코드가 노출될 가능성이 있을 때 접근제어를 이용한다."}]},{"type":"paragraph","inlineContent":[{"text":"🔍 ","type":"text"},{"inlineContent":[{"type":"text","text":"접근수준"}],"type":"strong"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"접근 수준 키워드를 통해 구현할 수 있다."}]}]},{"content":[{"inlineContent":[{"text":"각 타입에 특정 접근수준을 지정할 수 있고, 타입 내부의 프로퍼티, 메서드, 이니셜라이저에도 접근수준을 지정할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"접근수준을 명시할 수 있는 키워드는 open, public, internal, fileprivate, private 가 있다."}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/d513957a-2c10-40ae-a561-dc78ab370c06"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/b203185f-ec2f-4ff2-9b14-ebdca5650b17"}]},{"inlineContent":[{"type":"text","text":"🔍 "},{"type":"strong","inlineContent":[{"type":"text","text":"Swift의 접근제어는 모듈과 소스파일 기반으로 설계되었다."}]}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"모듈은 배포할 코드의 묶음 단위이다. 프레임워크, 라이브러리, 애플리케이션이 모두 모듈 단위가 될 수 있다. import 키워드를 사용해 불러올 수 있다."}]},{"inlineContent":[{"type":"text","text":"소스파일은 하나의 스위프트 소스코드 파일을 의미한다. 보통 파일하나에 타입하나만 정의하지만 때로는 소스파일 하나에 여러타입이나 함수 등 많은 것을 정의하거나 구현할 수도 있다."}],"type":"paragraph"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"🔍 private과 fileprivate 차이점"}]}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"같은 파일 내부에서 private 접근수준과 fileprivate 접근수준은 사용할 때 분명한 차이가 있다. fileprivate 접근수준으로 지정한 요소는 같은 파일 어떤 코드에서도 접근할 수 있다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"반면에 private 접근수준으로 지정한 요소는 같은 파일 내부에 다른 타입의 코드가 있더라도 접근이 불가능합니다. 그러나 자신을 확장하는 익스텐션(extension) 코드가 같은 파일에 존재하는 경우에는 접근할 수 있다."}]},{"inlineContent":[{"text":"🔍 ","type":"text"},{"inlineContent":[{"type":"text","text":"접근제어 구현 참고사항"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"상위 요소보다 하위 요소가 더 높은 접근수즌을 가질 수 없다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"함수의 매개변수로 특정 접근수준이 부여된 타입이 전달되거나 반환된다면 그 타입의 접근수준보다 함수의 접근수준이 높게 설정될 수 없다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"프레임워크를 만들 때는 다른 모듈에서 특정 기능에 접근할 수 있도록 API로 사용할 기능을 공개 접근 수준으로 지정해주어야 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"열거형 내부의 각 case별로 따로 접근수준을 부여할 수 없고, 각 case의 접근수준은 열거형 자체 접근수준을 따른다."}]}]}]},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"🔍 읽기 전용 구현","type":"text"}]}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"값을 가져갈 수는 있지만 변경할 수 없도록 구현하고 싶을 때 설정자만 더 낮은 접근수준을 갖도록 제한하면 된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"요소의 접근수준 키워드 뒤에 접근수즌(set) 처럼 표현하면 설정자의 접근수준만 더 낮도록 지정해줄 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"설정자 접근수준 제한은 프로퍼티, 서브스크립트, 변수 등에 적용될 수 있으며, 해당 요소의 접근수준보다 같거나 낮은 수준으로 제한해주어야 한다."}]}]}],"type":"unorderedList"},{"text":"타입의 설계","level":2,"type":"heading","anchor":"%ED%83%80%EC%9E%85%EC%9D%98-%EC%84%A4%EA%B3%84"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"일반화(Generalization)","type":"text"}]},{"type":"text","text":" 인스턴스의 공통 특징을 뽑아내는 것"}]},{"inlineContent":[{"inlineContent":[{"text":"추상화(Abstraction)","type":"text"}],"type":"strong"},{"type":"text","text":" 공통 특성 중 관심이 있는 부분만 추출하고 나머지는 무시하는 과정"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"은닉화(Hiding)"}]},{"type":"text","text":" 주요 사항이 겉으로 드러나지 않도록 감추는 것"}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"캡슐화(Encapsulation)"}]},{"type":"text","text":" 중요사항을 감춘 상태에서 외부에서 그것을 사용할 수 있는 방법을 제공하고 외부와 소통을 하는 것"}]}]}],"schemaVersion":{"minor":3,"patch":0,"major":0},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/210816_----"]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/210816_----"},"sections":[],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://github.com/user-attachments/assets/d513957a-2c10-40ae-a561-dc78ab370c06":{"variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/d513957a-2c10-40ae-a561-dc78ab370c06","traits":["1x","light"]}],"identifier":"https:\/\/github.com\/user-attachments\/assets\/d513957a-2c10-40ae-a561-dc78ab370c06","alt":"31","type":"image"},"https://github.com/user-attachments/assets/b203185f-ec2f-4ff2-9b14-ebdca5650b17":{"identifier":"https:\/\/github.com\/user-attachments\/assets\/b203185f-ec2f-4ff2-9b14-ebdca5650b17","variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/b203185f-ec2f-4ff2-9b14-ebdca5650b17","traits":["1x","light"]}],"type":"image","alt":"32"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/210817_----.json b/docs/data/documentation/arinote/210817_----.json new file mode 100644 index 0000000..ba29195 --- /dev/null +++ b/docs/data/documentation/arinote/210817_----.json @@ -0,0 +1 @@ +{"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/210817_----"]}],"primaryContentSections":[{"kind":"content","content":[{"anchor":"TILToday-I-Learned","text":"TIL(Today I Learned)","level":1,"type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"날짜: 2021년 8월 17일"},{"text":" ","type":"text"},{"type":"text","text":"작성자: 이아리"},{"type":"text","text":" "},{"text":"태그: Protocol, 프로토콜","type":"text"}]},{"text":"학습내용","anchor":"%ED%95%99%EC%8A%B5%EB%82%B4%EC%9A%A9","level":2,"type":"heading"},{"type":"paragraph","inlineContent":[{"text":"오늘은 프로토콜에 대해서 알아보는 시간을 가졌다. 공부할 내용이 산더미이다. 메서드 부분까지만 배우고 이니셜라이저 관련한 프로토콜 채택방법도 배워야한다. 과제 3-3도 제출하긴 했으나 많이 모자른 것 같다. 내가 할 수 있는 최선을 다해 작성을 해보았는데, 설계 과정이 쉬운일이 아니라는 걸 새삼 깨달았다. 나름 그래도 정리해서 설계해보겠다고 메모장에 만들 프로퍼티랑 메서드를 적어가며 코드를 작성해보았는데… 작성할때마다 계속 수정을 해야하는[?]…. 코드에 손이 많이갔다. 그만큼 설계과정에서 완벽하지 못했다는 이야기겠지… 설계하는 과정에서도 아직 이니셜라이저, 프로퍼티, 메서드 부분 개념이 완벽하지 못하다는 것도 알게되었다. ‘뭐 이정도면 다 아는거겠지!!?’ 라고 생각하고 있었는데, 그것은 오산이였다. 그래서 심화과제를 풀어가면서 프로퍼티와 메서드, 이니셜라이저 부분을 다시한번 복습했다. 봐도봐도 익숙해지지가 않는데, 계속 복습 하다보면 고수가 되는 날이 오겠지…","type":"text"}]},{"text":"정리","type":"heading","anchor":"%EC%A0%95%EB%A6%AC","level":2},{"type":"paragraph","inlineContent":[{"text":"오늘 정리는 과제를 하느라 프로토콜만 간신히 정리하였다. 그 마저도 다한게 아니라 메서드 부분까지만 정리해서 아직 미완성이다. 과제랑 글쓰기를 같이 하려니까 많이 벅차네…","type":"text"}]},{"text":"프로토콜이란?","level":3,"anchor":"%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%9D%B4%EB%9E%80","type":"heading"},{"inlineContent":[{"text":"프로토콜(Protocol)은 인터페이스이다. 최소한으로 가져야할 속성이나 메서드를 정의해줄 수 있다. 구현은 하지않고 정의만 하는게 특징이다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"클래스와 구조체에 프로토콜을 적용시킬 수 있다. 프로토콜을 적용하면 프로토콜에서 정의한 속성과 메서드를 모두 구현해야 한다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"text":"하나의 타입으로 사용되기 때문에 아래와 같이 타입 사용이 허용되는 모든 곳에 프로토콜을 사용할 수 있다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"함수, 메소드, 이니셜라이저의 파라미터 타입 혹은 리턴 타입","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"상수, 변수, 프로퍼티의 타입"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"배열, 딕셔너리의 원소타입"}]}]}]},{"inlineContent":[{"type":"text","text":"기본형태"}],"type":"paragraph"},{"syntax":"swift","code":["protocol name {"," \/\/ 프로토콜 정의","}"],"type":"codeListing"},{"type":"paragraph","inlineContent":[{"type":"text","text":"구조체, 클래스, 열거형 등에서 프로토콜을 채택하려면 타입 이름 뒤에 콜론”:”을 붙여준 후 채택할 프로토콜 이름을 쉼표”,”로 구분하여 명시해준다. SubClass의 경우 SuperClass를 가장 앞에 명시한다."}]},{"syntax":"swift","type":"codeListing","code":["struct SomeStruct: AProtocol, AnotherProtocol {"," \/\/ 구조체 정의","}","\/\/ 상속받는 클래스의 프로토콜 채택","class SomeClass: SuperClass, AProtocol, AnotherProtocol {"," \/\/ 클래스 정의","}"]},{"type":"paragraph","inlineContent":[{"text":"프로토콜을 생성할때 AnyObject를 상속하게되면 클래스 전용 프로토콜로 정의해줄 수 있다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"구조체나 열거형에서는 채택할 수 없는 프로토콜이 된다."}]},{"code":["protocol Protocols { \/\/ Upper Camel Case"," \/\/ property Requirements"," \/\/ method Requirements"," \/\/ initializer Requirements"," \/\/ subscript Requirements","}","","protocol Name: Protocols {"," \/\/ 프로토콜은 상속을 지원한다. 클래스와 문법은 동일하다."," \/\/ 클래스와 다르게 다중상속을 지원한다.","}","","protocol Test {"," func say()","}","","struct A: Test {"," func say() {"," \/\/ 구현부는 자유롭게 작성할 수 있다."," print(\"난 말할 수 있다!!!\")"," }","}","","protocol OnlyClass: AnyObject, Test {"," \/\/ 클래스 전용 프로토콜로 선언된다."," \/\/ 구조체나 열거형에서는 채택할 수 없다.","}","","\/\/struct Value: OnlyClass {","\/\/ \/\/ error: non-class type 'Value' cannot conform to class protocol 'OnlyClass'","\/\/}","class Reference: OnlyClass {"," \/\/ OnlyClass 프로토콜은 AnyObject외에 Test 프로토콜도 상속받고 있다."," func say() { \/\/ 그래서 Test의 정의되어있는 메서드 구현이 필요하다"," print(\"난 클래스야, 참조타입임!!!\")"," }","}"],"type":"codeListing","syntax":"swift"},{"level":3,"anchor":"Property-Requirments","type":"heading","text":"Property Requirments"},{"inlineContent":[{"type":"text","text":"프로토콜에서는 프로퍼티가 저장프로퍼티인지 연산프로퍼티인지 명시하지 않고, 이름과 타입 그리고 gettable, settable한지 명시한다. 프로퍼티는 항상 var로 선언해야 한다. 프로토콜에서의 var 키워드는 가변성과는 아무런 관계가 없다. 대신 선언하는 멤버가 프로퍼티라는 것을 나타낸다."}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["protocol Protocols { "," var name: String { get set }"," static var age: Int { get set }","}"]},{"type":"paragraph","inlineContent":[{"text":"get과 set이 모두 포함되어있다면 형식에서 읽기와 쓰기가 가능한 프로퍼티로 구현해야하고, get만 포함되어있는 경우에는 읽기 가능하도록 구현하면 된다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"text":"타입 프로퍼티로 선언할때는 var 키워드 앞에 static을 추가해주면 된다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"get만 설정해주었을 때"}]},{"syntax":"swift","code":["protocol Test {"," var name: String { get }","}","","struct A: Test {"," var name: String \/\/ 읽기만 가능하도록 구현","}","struct B: Test {"," var name: String = \"Ari Lee\" \/\/ 반드시 읽기만 가능하도록 구현하지 않아도 된다.","}"],"type":"codeListing"},{"type":"paragraph","inlineContent":[{"type":"text","text":"get set 둘다 설정해주었을 때"}]},{"type":"codeListing","syntax":"swift","code":["protocol TestT {"," var name: String { get set }","}","","struct C: TestT {"," \/*"," let name: String = \"Ari Lee\" note: candidate is not settable, but protocol requires it"," ~~~ ^"," var"," *\/"," var name: String = \"Ari Lee\" \/\/ 읽기 쓰기 모두 가능해야하기 때문에 let 선언은 불가하다.","}","struct D: TestT {"," var name: String {"," get {"," return \"Ari Lee\""," }"," set {"," "," }"," }","}"]},{"type":"paragraph","inlineContent":[{"type":"text","text":"static을 이용하여 타입 프로퍼티를 정의해줬을 때."},{"text":" ","type":"text"},{"text":"주의 : 클래스 안에 static키워드로 선언된 프로퍼티는 서브클래스로 상속은 가능하지만 오버라이딩은 불가능하다. 따라서 오버라이딩을 허용하려면 static 대신 class 타입으로 선언해줘야 한다.","type":"text"}]},{"type":"codeListing","syntax":"swift","code":["protocol TestTe {"," static var name: String { get set }","}","","struct E: TestTe {"," static var name: String = \"Ari Lee\" \/\/ 프로퍼티 앞에 타입을 필수적으로 추가해줘야 한다.","}","","class F: TestTe {","\/\/ static var name: String {","\/\/ get {","\/\/ return \"Ari Lee\"","\/\/ }","\/\/ set {","\/\/","\/\/ }","\/\/ }","\/\/ static 타입은 상속은 가능하지만 오버라이딩은 불가능하다"," "," class var name: String {","\/\/ static에서 class로 변경하여도 여전히 타입 프로퍼티이다. 이름과 자료형, 가변성도 동일하다."," get {"," return \"Ari Lee\""," }"," set {"," "," }"," }","\/\/ 그래서 프로토콜의 요구사항을 충족시킨다. 서브클래스에서 오버라이딩하는 것도 가능하다.","}"]},{"level":3,"text":"Method Requirements","type":"heading","anchor":"Method-Requirements"},{"code":["protocol Protocols {"," func A(param: Int) -> Int"," static func B(param: String) -> String"," mutating func C(param: Double) -> Double \/\/ 값타입 전용은 아니다. 참조타입에서도 채택가능하다.","}"],"type":"codeListing","syntax":"swift"},{"type":"paragraph","inlineContent":[{"type":"text","text":"프로토콜은 메서드 이름, 파라미터 이름과 타입, 리턴 타입만 정의한다."}]},{"type":"paragraph","inlineContent":[{"text":"채택하여 메서드를 구현할때 구현부는 자유롭게 작성할 수 있다.","type":"text"}]},{"syntax":"swift","type":"codeListing","code":["protocol Talk {"," func say()","}","","class A: Talk {"," func say() {"," print(\"난 클래스 A라고 해.\")"," }","}"]},{"inlineContent":[{"text":"만약 구조체안에 메서드가 프로퍼티의 값을 바꾸려한다면 mutating 키워드를 붙여줘야 한다.","type":"text"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"그럼 프로토콜도 마찬가지로 mutating 키워드를 추가해줘야 한다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"이때 클래스는 참조타입이기 때문에 mutating 키워드를 메서드 앞에 붙여주지 않아도 프로토콜 요구사항을 충족시키기 때문에 에러가 발생하지 않는다."}]},{"inlineContent":[{"type":"text","text":"예제"}],"type":"paragraph"},{"type":"codeListing","code":["protocol renamed {"," mutating func newName()","}","","struct A: renamed {"," var name: String = \"Ari\""," mutating func newName() {"," name = \"three\""," }","}","","class B: renamed {"," var name: String = \"Ari\""," func newName() {"," name = \"three\""," }","}"],"syntax":"swift"},{"inlineContent":[{"type":"text","text":"오버로딩 규칙에 따라서 이름이 동일한 메서드를 인스턴스 메서드와 타입 메서드로 동시에 구현할 수도 있다."}],"type":"paragraph"},{"type":"codeListing","code":["protocol Test {"," static func test()","}","","class C: Test {"," var number = 0"," func test() {"," number = 10"," }"," ","\/\/ static func test() {","\/\/ \/\/ 이 메서드는 서브클래스에 상속은 가능하지만 오버라이딩은 불가능하다.","\/\/ }"," "," class func test() {"," \/\/ 이 메서드는 프로토콜을 충족하면서 상속과 오버라이딩도 가능하다."," }","}"],"syntax":"swift"}]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/210817_----","interfaceLanguage":"swift"},"schemaVersion":{"minor":3,"major":0,"patch":0},"metadata":{"role":"article","roleHeading":"Article","title":"210817 프로토콜","modules":[{"name":"Ari's Notes"}]},"kind":"article","sections":[],"references":{"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/210820_------,_----_---.json b/docs/data/documentation/arinote/210820_------,_----_---.json new file mode 100644 index 0000000..5392a9d --- /dev/null +++ b/docs/data/documentation/arinote/210820_------,_----_---.json @@ -0,0 +1 @@ +{"kind":"article","primaryContentSections":[{"kind":"content","content":[{"anchor":"TILToday-I-Learned","text":"TIL(Today I Learned)","type":"heading","level":1},{"type":"paragraph","inlineContent":[{"type":"text","text":"날짜: 2021년 8월 20일"},{"type":"text","text":" "},{"type":"text","text":"작성자: 이아리"},{"text":" ","type":"text"},{"type":"text","text":"태그: Class, initalizer, struct"}]},{"level":2,"text":"학습내용","type":"heading","anchor":"%ED%95%99%EC%8A%B5%EB%82%B4%EC%9A%A9"},{"inlineContent":[{"text":"오늘은 이니셜라이저 복습과 공부하면서 알게되었고 개념을 정확히 모르고있는 키워드들을 구글링하면서 정리해보았다. 이니셜라이저는 구조체의 경우 단순하고 간단하지만 클래스로 넘어가게되면 상속 때문에 좀 헷갈리는 부분이 많다. 오늘 복습하게 되면서 개념이 어느정도 정리가 되긴 하였지만, 아직 좀더 반복학습이 필요할 것 같다. 특히 2단계 초기화 부분이 약간 이해하는데 어려움을 겪었다. 직접 코드 작성을 해보고 어떤 순서로 초기화가 되는지를 설명할 수 있을 때까지 공부를 좀 더 해봐야할 것 같다. 또 과제를 진행하면서 구조체와 클래스는 어떤 상황에 사용해야 적합할지를 생각하다가 공식문서에서 ‘직렬화’할때 사용을 권장한다고 되어있어서 직렬화가 무엇인지를 공부해보았다. 쉽게 말해서 시스템 외부로 데이터를 전송하기 위해 복원할 수 있는 형태의 데이터로 전환하는 과정이라고 말할 수 있겠다. 인코딩과 같은 형태이다. JSON도 들어만 보고 어떤 형태인지만 봤지, 정확히 어떤 것인지 무엇을 말하는 건지 어떤 상황에 쓰는 것인지 뜻을 몰라서 검색했다. 앞서 말한 것과 같이 나는 오늘 복습과 궁금증을 해결하는 시간을 가졌다. 학습을 하는 것도 중요하지만 복습도 정말 중요하다!","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/c0957009-b8a1-4ac6-8190-b49dabaae84b"}]},{"inlineContent":[{"text":"그리고 뜬금없지만 iOS 개발자 로드맵 보다가 내가 이 짧은 기간에 여기까지의 지식을 채웠다는게 대단하다고 느껴졌다. 뭐 물론 완벽한 상태는 아니지만… 🤣 얼릉 고수가 되고싶어!","type":"text"}],"type":"paragraph"},{"text":"initalizer","anchor":"initalizer","level":2,"type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"🔍 "},{"type":"strong","inlineContent":[{"type":"text","text":"초기화 키워드 정리"}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"Designated initalizer (지정 초기화)"}]},{"type":"text","text":" "},{"type":"text","text":"기본 이니셜라이저이다. 클래스 안에 있는 모든 프로퍼티를 초기화 될 수 있도록 해줘야 한다."}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"Convenience initalizer (편의 초기화)","type":"text"}]},{"text":" ","type":"text"},{"text":"지정 초기화의 일부 파라미터의 기본값을 설정하여 초기화하는 이니셜라이저이다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"쉽게 말해서 보조 초기화라고 생각하면 될 것 같다. 적은 입력으로 초기화를 편리하게 할 수 있게 도와주는 역할을 한다**.**"}]},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"Required initalizer (필수 초기화)","type":"text"}]},{"text":" ","type":"text"},{"text":"이니셜라이저의 재정의를 필수적으로 해줘야 한다는 것을 알려주기 위한 이니셜라이저이다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"선언하게되면 해당 이니셜라이저는 클래스에서 반드시 구현해야한다. 클래스를 상속받은 클래스에서 구현할 때에도 required 키워드를 붙여 사용해야 한다."},{"text":" ","type":"text"},{"text":"참고로 override를 기본으로 포함하고 있다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"deinitalizer (초기화 해제)"}]},{"text":" ","type":"text"},{"type":"text","text":"deinit은 클래스의 인스턴스가 메모리에서 해제되는 시점에 호출된다."},{"type":"text","text":" "},{"type":"text","text":"파라미터를 지닐 수 없고, 자동으로 호출되므로 직접 호출하는 것이 불가능하다."},{"type":"text","text":" "},{"type":"text","text":"클래스 타입에서만 구현할 수 있고 인스턴스가 메모리에서 해제되는 시점은 ARC의 규칙에 따라 결정된다."}]},{"inlineContent":[{"type":"text","text":"🔍 "},{"inlineContent":[{"text":"초기화 형태 정리","type":"text"}],"type":"strong"}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["struct A {"," var str: String = \"text\"","}","","let own = A() \/\/ Default initalizer","let two = A(str: \"Ari\") \/\/ Memberwise initalizer"]},{"inlineContent":[{"inlineContent":[{"type":"text","text":"Default initalizer"}],"type":"strong"},{"text":"","type":"text"},{"type":"text","text":" "},{"type":"text","text":"프로퍼티가 모두 초기화 되어있는 경우 매개변수가 없는 이니셜라이저가 제공된다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"Memberwise initalizer","type":"text"}],"type":"strong"},{"text":" ","type":"text"},{"type":"text","text":"구조체 내에 프로퍼티가 하나도 초기화 되지 않은 경우 초기값이 없는 속성을 파라미터로 갖는 Memberwise initalizer가 제공된다. 모두 let으로 기본값으로 초기화 되어있는 경우라면 Memberwise initalizer는 제공되지 않는다."}]},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"initalizer Delegation"}]},{"type":"text","text":" (이니셜라이저 위임)"},{"text":" ","type":"text"},{"type":"text","text":"이니셜라이저는 다른 이니셜라이저를 호출하여 인스턴스 초기화의 일부를 수행할 수 있다. 이를 이니셜라이저 위임이라고 한다. 값타입인지 참조타입인지에 따라 Initializer delegation의 작동 방법이 달라진다."},{"type":"text","text":" "},{"type":"text","text":"값타입은 상속을 지원하지 않는다. 따라서 자신이 제공하는 생성자에게만 위임할 수 있기 때문에 구조가 단순하다."},{"text":" ","type":"text"},{"type":"text","text":"반면에 참조타입은 다른 클래스를 상속할 수 있기 때문에 상속하는 모든 프로퍼티가 초기화 중에 적절한 값이 할당되어야 한다."}],"type":"paragraph"},{"type":"thematicBreak"},{"text":"타입을 설계할 때 참고할 단계 정리","level":2,"anchor":"%ED%83%80%EC%9E%85%EC%9D%84-%EC%84%A4%EA%B3%84%ED%95%A0-%EB%95%8C-%EC%B0%B8%EA%B3%A0%ED%95%A0-%EB%8B%A8%EA%B3%84-%EC%A0%95%EB%A6%AC","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"일반화(Generalization)"}]},{"type":"text","text":" 인스턴스의 공통 특징을 뽑아내는 것"},{"type":"text","text":" "},{"type":"strong","inlineContent":[{"text":"추상화(Abstraction)","type":"text"}]},{"type":"text","text":" 공통 특성 중 관심이 있는 부분만 추출하고 나머지는 무시하는 과정"},{"text":" ","type":"text"},{"inlineContent":[{"type":"text","text":"은닉화(Hiding)"}],"type":"strong"},{"type":"text","text":" 주요 사항이 겉으로 드러나지 않도록 감추는 것"},{"type":"text","text":" "},{"inlineContent":[{"text":"캡슐화(Encapsulation)","type":"text"}],"type":"strong"},{"type":"text","text":" 중요사항을 감춘 상태에서 외부에서 그것을 사용할 수 있는 방법을 제공하고 외부와 소통을 하는 것"}]},{"type":"thematicBreak"},{"level":2,"anchor":"%EA%B6%81%EA%B8%88%ED%96%88%EB%8D%98-%ED%82%A4%EC%9B%8C%EB%93%9C-%EC%A0%95%EB%A6%AC","type":"heading","text":"궁금했던 키워드 정리"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"override","type":"text"}]},{"type":"text","text":" "},{"text":"상속된 멤버를 현재클래스에 적합하게 다시 구현할 때 사용하는 것","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"overloading","type":"text"}]},{"text":" ","type":"text"},{"type":"text","text":"하나의 타입에서 동일한 이름을 가진 다수의 멤버를 구현할 때 사용한다."},{"text":" ","type":"text"},{"text":"이름이 동일하면 파라미터 수로 식별","type":"text"},{"text":" ","type":"text"},{"text":"함수이름, 파라미터 수가 동일하면 파라미터 타입으로 식별","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"함수이름, 파라미터가 동일하면 Argument Label로 식별"},{"text":" ","type":"text"},{"text":"함수이름, 파라미터, Argument Label이 동일하면 리턴타입으로 식별","type":"text"}]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"nan"}],"type":"strong"},{"type":"text","text":" "},{"type":"text","text":"숫자가 아닌 값. 숫자가 아닌 값을 연산하였을 때 오류로 나타내는 반환 값이다."}]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"Serialization","type":"text"}],"type":"strong"},{"type":"text","text":" "},{"type":"text","text":"직렬화는 객체 등 특정 구조의 데이터를 이후 복원할 수 있는 형태의 데이터로 전환하는 과정."},{"text":" ","type":"text"},{"text":"역직렬화는 그 반대 직렬화된 데이터를 복원하는 과정이다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"직렬화된 데이터는 아무리 길어도 1줄 정도이다."},{"type":"text","text":" "},{"type":"text","text":"직렬화 하는 이유는 해당 데이터를 시스템 외부에 저장하거나 전송하기 위해서 사용한다. 역직렬화는 시스템 외부에 저장되거나 전송된 데이터를 받아와 복구 후 사용한다."},{"text":" ","type":"text"},{"text":"직렬화 == 인코딩 \/ 역직렬화 == 디코딩 쉽게 이해하자면 이렇다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"JSON"}]},{"text":" ","type":"text"},{"text":"JavaScript Object Notation라는 의미의 축약어로 데이터를 저장하거나 전송할 때 많이 사용되는 경량의 데이터 교환 방식이다. JSON 표현식은 사람과 기계 모두 이해하기 쉬우며 용량이 작아서 최근에는 JSON이 XML을 대체해서 데이터 전송 등에 많이 사용한다. JSON은 데이터 포맷일 뿐이며 어떠한 통신 방법도, 프로그래밍 문법도 아닌 단순히 데이터를 표시하는 표현 방법일 뿐이다.","type":"text"}]},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"lazy"}]}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"사용 전 고려사항"}]},{"type":"text","text":" 필요하지만 사용하기도 전에 불러오기 부담스러운 것들은 lazy를 통해 선언해준다."},{"type":"text","text":" "},{"type":"text","text":"실제로 사용될 때까지 프로퍼티 생성을 지연시킬 수 있고 속성들을 연산 프로퍼티 처럼 만든다."},{"type":"text","text":" "},{"type":"text","text":"반드시 var와 함께 쓰여야한다."},{"type":"text","text":" "},{"type":"text","text":"기본적으로 구조체랑 클래스에서만 사용할 수 있다."},{"text":" ","type":"text"},{"text":"연산 프로퍼티에서는 사용할 수 없다.","type":"text"},{"text":" ","type":"text"},{"text":"클로저와 함께 사용할 수 있다.","type":"text"}]},{"level":2,"type":"heading","text":"복습하면서 블로그 글을 좀더 보충해주었다.","anchor":"%EB%B3%B5%EC%8A%B5%ED%95%98%EB%A9%B4%EC%84%9C-%EB%B8%94%EB%A1%9C%EA%B7%B8-%EA%B8%80%EC%9D%84-%EC%A2%80%EB%8D%94-%EB%B3%B4%EC%B6%A9%ED%95%B4%EC%A3%BC%EC%97%88%EB%8B%A4"},{"inlineContent":[{"identifier":"https:\/\/leeari95.tistory.com\/19#%F0%9F%94%8D%C2%A0-initalizer-extension-for-value-types","type":"reference","isActive":true},{"type":"reference","isActive":true,"identifier":"https:\/\/leeari95.tistory.com\/20#%E2%9C%94%EF%B8%8F-%EC%B0%B8%EC%A1%B0%ED%83%80%EC%9E%85%EC%9D%98%C2%A0initalizer-delegation"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/leeari95.tistory.com\/20#%E2%9C%94%EF%B8%8F-2%EB%8B%A8%EA%B3%84-%EC%B4%88%EA%B8%B0%ED%99%94","isActive":true,"type":"reference"}]},{"inlineContent":[{"identifier":"https:\/\/leeari95.tistory.com\/17#%E2%9C%94%EF%B8%8F%C2%A0apple%EC%9D%B4-%EA%B6%8C%EC%9E%A5%ED%95%98%EB%8A%94-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EA%B5%AC%EC%A1%B0%EC%B2%B4-%EC%9A%A9%EB%8F%84","type":"reference","isActive":true}],"type":"paragraph"}]}],"sections":[],"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"210820 이니셜라이저, 궁금했던 키워드"},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/210820_------,_----_---","interfaceLanguage":"swift"},"schemaVersion":{"patch":0,"minor":3,"major":0},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"variants":[{"paths":["\/documentation\/arinote\/210820_------,_----_---"],"traits":[{"interfaceLanguage":"swift"}]}],"references":{"https://leeari95.tistory.com/17#%E2%9C%94%EF%B8%8F%C2%A0apple%EC%9D%B4-%EA%B6%8C%EC%9E%A5%ED%95%98%EB%8A%94-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EA%B5%AC%EC%A1%B0%EC%B2%B4-%EC%9A%A9%EB%8F%84":{"identifier":"https:\/\/leeari95.tistory.com\/17#%E2%9C%94%EF%B8%8F%C2%A0apple%EC%9D%B4-%EA%B6%8C%EC%9E%A5%ED%95%98%EB%8A%94-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EA%B5%AC%EC%A1%B0%EC%B2%B4-%EC%9A%A9%EB%8F%84","title":"https:\/\/leeari95.tistory.com\/17#✔️ apple이-권장하는-클래스와-구조체-용도","titleInlineContent":[{"text":"https:\/\/leeari95.tistory.com\/17#✔️ apple이-권장하는-클래스와-구조체-용도","type":"text"}],"type":"link","url":"https:\/\/leeari95.tistory.com\/17#%E2%9C%94%EF%B8%8F%C2%A0apple%EC%9D%B4-%EA%B6%8C%EC%9E%A5%ED%95%98%EB%8A%94-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%99%80-%EA%B5%AC%EC%A1%B0%EC%B2%B4-%EC%9A%A9%EB%8F%84"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://leeari95.tistory.com/20#%E2%9C%94%EF%B8%8F-%EC%B0%B8%EC%A1%B0%ED%83%80%EC%9E%85%EC%9D%98%C2%A0initalizer-delegation":{"identifier":"https:\/\/leeari95.tistory.com\/20#%E2%9C%94%EF%B8%8F-%EC%B0%B8%EC%A1%B0%ED%83%80%EC%9E%85%EC%9D%98%C2%A0initalizer-delegation","titleInlineContent":[{"text":"https:\/\/leeari95.tistory.com\/20#✔️-참조타입의 initalizer-delegation","type":"text"}],"title":"https:\/\/leeari95.tistory.com\/20#✔️-참조타입의 initalizer-delegation","url":"https:\/\/leeari95.tistory.com\/20#%E2%9C%94%EF%B8%8F-%EC%B0%B8%EC%A1%B0%ED%83%80%EC%9E%85%EC%9D%98%C2%A0initalizer-delegation","type":"link"},"https://leeari95.tistory.com/20#%E2%9C%94%EF%B8%8F-2%EB%8B%A8%EA%B3%84-%EC%B4%88%EA%B8%B0%ED%99%94":{"identifier":"https:\/\/leeari95.tistory.com\/20#%E2%9C%94%EF%B8%8F-2%EB%8B%A8%EA%B3%84-%EC%B4%88%EA%B8%B0%ED%99%94","title":"https:\/\/leeari95.tistory.com\/20#✔️-2단계-초기화","titleInlineContent":[{"text":"https:\/\/leeari95.tistory.com\/20#✔️-2단계-초기화","type":"text"}],"type":"link","url":"https:\/\/leeari95.tistory.com\/20#%E2%9C%94%EF%B8%8F-2%EB%8B%A8%EA%B3%84-%EC%B4%88%EA%B8%B0%ED%99%94"},"https://github.com/user-attachments/assets/c0957009-b8a1-4ac6-8190-b49dabaae84b":{"identifier":"https:\/\/github.com\/user-attachments\/assets\/c0957009-b8a1-4ac6-8190-b49dabaae84b","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/user-attachments\/assets\/c0957009-b8a1-4ac6-8190-b49dabaae84b"}],"alt":"33","type":"image"},"https://leeari95.tistory.com/19#%F0%9F%94%8D%C2%A0-initalizer-extension-for-value-types":{"identifier":"https:\/\/leeari95.tistory.com\/19#%F0%9F%94%8D%C2%A0-initalizer-extension-for-value-types","title":"https:\/\/leeari95.tistory.com\/19#🔍 -initalizer-extension-for-value-","titleInlineContent":[{"text":"https:\/\/leeari95.tistory.com\/19#🔍 -initalizer-extension-for-value-","type":"text"}],"type":"link","url":"https:\/\/leeari95.tistory.com\/19#%F0%9F%94%8D%C2%A0-initalizer-extension-for-value-types"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/210827_filemanger.json b/docs/data/documentation/arinote/210827_filemanger.json new file mode 100644 index 0000000..9c71385 --- /dev/null +++ b/docs/data/documentation/arinote/210827_filemanger.json @@ -0,0 +1 @@ +{"kind":"article","schemaVersion":{"major":0,"patch":0,"minor":3},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"metadata":{"role":"article","roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"title":"210827 FileManger"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/210827_filemanger"]}],"sections":[],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/210827_FileManger","interfaceLanguage":"swift"},"primaryContentSections":[{"content":[{"anchor":"TILToday-I-Learned","text":"TIL(Today I Learned)","level":1,"type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"날짜: 2021년 8월 27일"},{"text":" ","type":"text"},{"type":"text","text":"작성자: 이아리"},{"type":"text","text":" "},{"text":"태그: Data, FileMenager, URL","type":"text"}]},{"text":"학습내용","type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5%EB%82%B4%EC%9A%A9"},{"type":"paragraph","inlineContent":[{"text":"진짜 ‘파일 읽고 쓰기’ 부분이 너무 헷갈리고 어려웠다. 헷갈린 부분을 해결하였더니 파일매니저라는 타입이 ‘꽤 직관적이구나’를 깨닫게 되었다. 캠프에서 주어진 예제 또한 합쳐진 예제가 아니라 각 동작에 대한 설명을 위한 예제인 것이였다. URL도 계속 헷갈렸는데.. 이게 FileManeger에선 ‘파일의 경로’를 뜻하는 거였다.. 난 그것도 모르고 위키백과를 뒤져가며 URL이 어떤 형태로 이루어져 있는지도 학습했다. 파일 경로인줄도 모르고 http 뒤질뻔..","type":"text"}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/e203c102-68bf-40bb-9f8a-c96809e29a3a"}],"type":"paragraph"},{"inlineContent":[{"text":"이번 4주차에 있는 ‘파일 읽고 쓰기’ 부분은 말 그대로 FileManager를 통해 파일을 읽고 쓰는 기초적인 방법을 배워보는 그런 시간이였다. 나만 그런게 아니라 다들 처음 배울때는 많이 헷갈려하는 것 같았다. (블로그를 염탐해보니…) 그 중에서 엄청 설명이 상세하고 자세히 되어있는 블로그를 보고 축복받은 느낌이 들었다. 나도 훗날 나같은 사람을 위해서 FileManager에 대한 상세한 포스팅을 작성하게 되었다.","type":"text"}],"type":"paragraph"},{"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","level":2,"type":"heading","text":"해결방법"},{"inlineContent":[{"type":"text","text":"코드가 잘실행이 되는지 안되는지 Xcode 플레이그라운드에 붙여넣었다. 에러가 나는 부분을 찾아서 빈 부분을 채웠더니 해결이 되었다. 그리고 코드 한줄 한줄 어떤 동작을 하는지도 주석처리하여 설명을 덧붙여넣었더니 이해를 하는데 많은 도움이 되었다. 메소드가 어떤일을 하는지도 상세히 알게되었고, 앞으로 코드가 이해되지 않는다면 이렇게 하나씩 찾아봐야될 것 같다."}],"type":"paragraph"},{"inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/user-attachments\/assets\/4e062f3d-68cb-487a-9e98-c4f733539544"}],"type":"paragraph"},{"inlineContent":[{"text":"휴… 정말 헷갈리고 궁금해서 미쳐돌아가는 줄 알았다. 잠들기 전부터 눈뜨는 그 순간까지 계속 물음표를 던지며 빨리 궁금증을 해결하고 싶어 오늘 오전 중에 오동나무에게 질문을 해가며 궁금증을 해소하였다.","type":"text"}],"type":"paragraph"},{"type":"heading","text":"오늘의 글쓰기","anchor":"%EC%98%A4%EB%8A%98%EC%9D%98-%EA%B8%80%EC%93%B0%EA%B8%B0","level":2},{"inlineContent":[{"isActive":true,"identifier":"https:\/\/leeari95.tistory.com\/32","type":"reference"}],"type":"paragraph"}],"kind":"content"}],"references":{"https://github.com/user-attachments/assets/4e062f3d-68cb-487a-9e98-c4f733539544":{"identifier":"https:\/\/github.com\/user-attachments\/assets\/4e062f3d-68cb-487a-9e98-c4f733539544","alt":"35","type":"image","variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/4e062f3d-68cb-487a-9e98-c4f733539544","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://github.com/user-attachments/assets/e203c102-68bf-40bb-9f8a-c96809e29a3a":{"alt":"34","identifier":"https:\/\/github.com\/user-attachments\/assets\/e203c102-68bf-40bb-9f8a-c96809e29a3a","type":"image","variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/e203c102-68bf-40bb-9f8a-c96809e29a3a","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://leeari95.tistory.com/32":{"url":"https:\/\/leeari95.tistory.com\/32","title":"https:\/\/leeari95.tistory.com\/32","titleInlineContent":[{"text":"https:\/\/leeari95.tistory.com\/32","type":"text"}],"identifier":"https:\/\/leeari95.tistory.com\/32","type":"link"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/210831_result.json b/docs/data/documentation/arinote/210831_result.json new file mode 100644 index 0000000..406e397 --- /dev/null +++ b/docs/data/documentation/arinote/210831_result.json @@ -0,0 +1 @@ +{"variants":[{"paths":["\/documentation\/arinote\/210831_result"],"traits":[{"interfaceLanguage":"swift"}]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"kind":"article","sections":[],"metadata":{"roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"title":"210831 Result","role":"article"},"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (today i learned)","anchor":"TIL-today-i-learned","level":1,"type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"날짜: 2021년 8월 31일"},{"text":" ","type":"text"},{"type":"text","text":"작성자: 이아리"},{"type":"text","text":" "},{"type":"text","text":"태그: Result, 에러처리"}]},{"text":"학습내용","anchor":"%ED%95%99%EC%8A%B5%EB%82%B4%EC%9A%A9","type":"heading","level":2},{"type":"paragraph","inlineContent":[{"type":"text","text":"갑자기 Result에 대해 알게되어 공부해보았는데, 이걸 왜 사용하는지 완벽히 이해하려면 공부가 좀 더 필요할 것 같다. 실제 API를 주고받을 때 자주 사용되는 에러 핸들링인 것 같다. 과제 코드에도 적용해보려고 했는데, 반환값이 Result라서, 혹은 Void라서 적용하기엔 적절치 못하다는 생각이 들었다. 내일은 새롭게 알게된 escaping 키워드와 비동기, 동기 관련한 키워드, 그리고 API가 정확히 무엇인지 상세한 공부를 해보면 좋을 것 같다."}]},{"text":"공부 내용 정리","level":2,"anchor":"%EA%B3%B5%EB%B6%80-%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC","type":"heading"},{"anchor":"Result-%ED%83%80%EC%9E%85","level":2,"type":"heading","text":"Result 타입"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/result","isActive":true,"type":"reference"}]},{"type":"paragraph","inlineContent":[{"text":"작업 중에는 실패할 수 있는 작업이 있는데, 디스크에 파일을 쓰거나, API를 호출하여 네트워크를 통해 데이터를 가져온다거나, 특정 URL에 있는 데이터를 불러오는 작업이 이 경우에 해당된다. 이런 실패 가능한 작업을 처리하기 위해 Swift에서는 do, try, catch문을 사용했었다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"text":"이 방법은 에러를 런타임에 동기로 자동으로 처리할 수 있게 해주었지만 이 방법으로는 발생가능한 여러가지 예외 상황에 대처하기 어려운 단점이 있다. Swift 5 에서는 이런 점을 보완하여 에러를 보다 유연하게 처리할 수 있는 Result문법을 지원하게 되었다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"성공과 실패에 대한 결과값을 반환하고 싶은 경우에 사용한다. 특히 비동기 API에서의 결과처리에 대한 복잡함을 간단하고 명확하게 만들어주고 쉽게 에러를 핸들링할 수 있도록 해준다."}]},{"type":"paragraph","inlineContent":[{"text":"Result 타입은 enum이며 두 개의 제네릭한 결과를 리턴하는 것이다. Failure 제네릭은 Error를 채택한 타입이여야 한다.","type":"text"}]},{"syntax":"swift","code":["@frozen public enum Result where Failure : Error {",""," \/\/\/ A success, storing a `Success` value."," case success(Success)",""," \/\/\/ A failure, storing a `Failure` value."," case failure(Failure)"],"type":"codeListing"},{"type":"paragraph","inlineContent":[{"type":"text","text":"예제로 이메일 형식을 검사하는 방법을 만들어보자."}]},{"inlineContent":[{"type":"text","text":"먼저 필수적으로 실패했을 경우의 타입을 정의해야한다."}],"type":"paragraph"},{"code":["enum EmailValidationError: Error {"," case cannotBeEmpty"," case invalid","}"],"syntax":"swift","type":"codeListing"},{"type":"paragraph","inlineContent":[{"type":"text","text":"이후 Result 타입을 반환하는 함수를 정의한다."}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"에러는 다음과 같이 정의한다."}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"문자열이 비어있는지?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정규식을 통하여 이메일 형식이 맞는지?"}]}]}],"type":"unorderedList"},{"type":"codeListing","code":["func validate(_ email: String?) -> Result {"," let regex = #\"([0-9a-zA-Z_-]+)@([0-9a-zA-Z_-]+)(\\.[0-9a-zA-Z_-]+){1,2}\"#"," "," guard let email = email else {"," return .failure(.cannotBeEmpty)"," }"," "," guard let range = email.range(of: regex, options: [.regularExpression]), (range.lowerBound, range.upperBound) == (email.startIndex, email.endIndex) else {"," return .failure(.invalid)"," }"," "," return .success(email)","}"],"syntax":"swift"},{"type":"paragraph","inlineContent":[{"text":"그런 다음 이메일 형식이 맞다면 이메일을 저장해주는 함수를 생성한다.","type":"text"}]},{"syntax":"swift","type":"codeListing","code":["func saveClicked(_ text: String?) {"," switch validate(text) {"," case .success(let email):"," emailSuccess = email"," print(\"이메일 저장에 성공하였습니다.\")"," case .failure(.cannotBeEmpty):"," print(\"이메일 주소는 비워둘 수 없습니다.\")"," case .failure(.invalid):"," print(\"이메일 주소 형식이 아닙니다. 다시 입력해주세요.\")"," }","}"]},{"inlineContent":[{"type":"text","text":"위와 같이 스위치문을 이용하여 두가지 케이스로 나누어 활용할 수도 있고,"}],"type":"paragraph"},{"syntax":"swift","code":["func saveClicked(_ text: String?) {"," do {"," let email = try validate(text).get()"," emailSuccess = email"," print(\"이메일 저장에 성공하였습니다.\")"," } catch let error as EmailValidationError {"," switch error {"," case .cannotBeEmpty:"," print(\"이메일 주소는 비워둘 수 없습니다.\")"," case .invalid:"," print(\"이메일 주소 형식이 아닙니다. 다시 입력해주세요.\")"," }"," } catch {"," print(\"문제가 발생하였습니다.\")"," }","}"],"type":"codeListing"},{"type":"paragraph","inlineContent":[{"type":"text","text":"do try catch문을 사용하여 구현할 수도 있다."}]},{"inlineContent":[{"type":"text","text":"Result 타입에는 get()메서드가 있다. 이 메서드는 성공한 값이 있으면 반환하고, 그렇지 않으면 에러를 throw 한다. get()메서드를 사용한 Result 타입 사용을 다음과 같이 예외 던지기로 변환할 수 있다."}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"아래 예제는 실제로 서버에 연결하지는 않지만 completion 핸들러를 사용하여 비동기 코드를 시뮬레이션 할 수 있다."}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["func validate(_ email: String?, completion: @escaping (Result) -> Void) {"," let regex = #\"([0-9a-zA-Z_-]+)@([0-9a-zA-Z_-]+)(\\.[0-9a-zA-Z_-]+){1,2}\"#"," "," guard let email = email else {"," return completion(.failure(.cannotBeEmpty))"," }"," "," guard let range = email.range(of: regex, options: [.regularExpression]), (range.lowerBound, range.upperBound) == (email.startIndex, email.endIndex) else {"," return completion(.failure(.invalid))"," }"," "," return completion(.success(email))","}","","validate(\"lee_ari95@icloud.com\") { result in"," switch result {"," case .success(let email):"," emailSuccess = email"," print(\"이메일 저장에 성공하였습니다.\")"," case .failure(.cannotBeEmpty):"," print(\"이메일 주소는 비워둘 수 없습니다.\")"," case .failure(.invalid):"," print(\"이메일 주소 형식이 아닙니다. 다시 입력해주세요.\")"," }","}"]},{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 Git을 다룰줄 몰라서 엘리의 유튜브 git 기초영상을 봤다가 강의까지 질러버렸다.. 열심히 해야지…!"}]},{"inlineContent":[{"identifier":"https:\/\/github.com\/user-attachments\/assets\/bac3603f-2022-494c-b0dc-82659e8cc187","type":"image"}],"type":"paragraph"}]}],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/210831_Result"},"schemaVersion":{"patch":0,"major":0,"minor":3},"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://github.com/user-attachments/assets/bac3603f-2022-494c-b0dc-82659e8cc187":{"identifier":"https:\/\/github.com\/user-attachments\/assets\/bac3603f-2022-494c-b0dc-82659e8cc187","variants":[{"url":"https:\/\/github.com\/user-attachments\/assets\/bac3603f-2022-494c-b0dc-82659e8cc187","traits":["1x","light"]}],"alt":"36","type":"image"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://developer.apple.com/documentation/swift/result":{"type":"link","title":"https:\/\/developer.apple.com\/documentation\/swift\/result","url":"https:\/\/developer.apple.com\/documentation\/swift\/result","titleInlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/swift\/result","type":"text"}],"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/result"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211004_---------,_--------------,_git----.json b/docs/data/documentation/arinote/211004_---------,_--------------,_git----.json new file mode 100644 index 0000000..8bf7916 --- /dev/null +++ b/docs/data/documentation/arinote/211004_---------,_--------------,_git----.json @@ -0,0 +1 @@ +{"metadata":{"modules":[{"name":"Ari's Notes"}],"role":"article","title":"211004 사전과제, 짝프로그래밍, Git기초","roleHeading":"Article"},"schemaVersion":{"patch":0,"major":0,"minor":3},"variants":[{"paths":["\/documentation\/arinote\/211004_---------,_--------------,_git----"],"traits":[{"interfaceLanguage":"swift"}]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211004_---------,_--------------,_Git----","interfaceLanguage":"swift"},"sections":[],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Git"]]},"primaryContentSections":[{"content":[{"anchor":"TIL-Today-I-Learned","type":"heading","level":1,"text":"TIL (Today I Learned)"},{"inlineContent":[{"type":"text","text":"10월 4일 (월)"}],"type":"paragraph"},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"type":"heading","text":"학습 내용"},{"inlineContent":[{"type":"text","text":" "},{"type":"text","text":" "},{"text":"오늘은 서로의 사전과제 코드를 직접 어떻게 구현하였는지 상세히 설명하는 시간을 가졌다. 이후 짝 프로그래밍을 통해서 사전과제 코드를 직접 손으로 써보고 중간 중간에 서로 피드백해주는 시간을 가졌다. 서로의 코드 한줄 한줄 보면서 왜그렇게 작성하였는지 궁금한 점을 나누었고 좀 더 개선할 수 있어보이는 부분은 피드백 해주었다. 짝꿍들에게 내가 알고있는 꿀팁 지식들을 나눌 수 있어서 기뻤다. 또 사전과제 면담 당시 받았던 피드백을 어떻게 고쳐나가면 좋을 지 좋은 생각이 떠오르기도 했던 유익한 시간이였다. 이후 야곰이 올려준 Git 강의를 보면서 예습을 진행하였다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":" "}],"type":"paragraph"},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","level":2,"type":"heading","text":"고민한 점"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "},{"type":"text","text":" "},{"text":"내 코드를 설명하면서 기억이 안나는 부분이 있어 애먹었다. 코드 설명 중간에 궁금해했던 부분도 말로 설명하려니 어떻게 설명하면 잘 전달될지 그 짧은 순간에 많은 고민을 하였다. 또 상대방의 코드를 보면서 꼼꼼히 피드백 해주고 싶었지만 이해가 안가는 부분이 있어서 정확한 피드백이 어려웠다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":" "}]},{"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","text":"해결방법","type":"heading","level":2},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"},{"type":"text","text":" "},{"type":"text","text":"짝 프로그래밍을 진행하면서 초반에 서로의 코드를 설명하고 피드백 해주는 시간에 봤던거보다 상대방의 코드가 더 잘 이해되기 시작하였다. 추가로 개선할 점이 보이게 되어 추가 피드백을 줄 수 있게 되었다. 또 내가 말로 설명하기 애매해서 휘리릭 넘어갔던 부분도 내 코드를 네비게이터가 되었을 때 다시 되짚어서 이해하기 쉽도록 재설명해주었다. 다들 잘 이해하고 넘어갔기를…."},{"text":" ","type":"text"},{"text":" ","type":"text"}]},{"anchor":"%EA%B3%B5%EB%B6%80%EB%82%B4%EC%9A%A9","type":"heading","text":"공부내용","level":2},{"inlineContent":[{"type":"text","text":" "},{"text":" ","type":"text"},{"type":"text","text":"이전에 엘리의 Git 마스터 강의를 보고 공부를 한 적이 있어서 기억을 되살려가며 다시한번 정리를 하는 시간을 가졌다."},{"text":" ","type":"text"},{"text":" ","type":"text"}],"type":"paragraph"},{"type":"thematicBreak"},{"level":3,"text":"명령어 정리","anchor":"%EB%AA%85%EB%A0%B9%EC%96%B4-%EC%A0%95%EB%A6%AC","type":"heading"},{"inlineContent":[{"text":"mkdir name","type":"text"},{"text":" ","type":"text"},{"text":"name 이란 이름을 가진 디렉토리를 생성한다","type":"text"}],"type":"paragraph"},{"inlineContent":[{"text":"cd","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"폴더들어가기"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"ls","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"파일목록보기"}]},{"type":"paragraph","inlineContent":[{"text":"git init","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"깃 초기화"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":".git 은 숨김파일이라는 뜻이다. 디렉토리 앞에 .이 붙으면 숨김폴더로 이해하면 될 것 같다."}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"open ."},{"text":" ","type":"text"},{"text":"현재 경로를 파인더로 열겠다","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"git st"},{"type":"text","text":" "},{"type":"text","text":"변경사항 확인하기"}]},{"inlineContent":[{"type":"text","text":"touch 파일이름.확장자"},{"text":" ","type":"text"},{"text":"파일만들기","type":"text"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"git add ."},{"type":"text","text":" "},{"type":"text","text":"모든 변경사항 추적하기"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"git config –global -e","type":"text"},{"text":" ","type":"text"},{"text":"깃 사용자 설정하기.","type":"text"}]},{"inlineContent":[{"text":"git commit -m “add a.txt file”","type":"text"},{"type":"text","text":" "},{"text":"커밋 등록하기","type":"text"}],"type":"paragraph"},{"type":"thematicBreak"},{"inlineContent":[{"type":"text","text":"touch .gitignore"},{"text":" ","type":"text"},{"type":"text","text":"이그노어 파일 만든후 안에 파일이름.확장자를 입력해주면 추적을 피할 수 있다."},{"text":" ","type":"text"},{"identifier":"https:\/\/i.imgur.com\/puDkCL0.png","type":"image"},{"text":" ","type":"text"},{"type":"image","identifier":"https:\/\/i.imgur.com\/drzPEEv.png"}],"type":"paragraph"},{"type":"thematicBreak"},{"text":"diff","level":3,"type":"heading","anchor":"diff"},{"type":"paragraph","inlineContent":[{"text":"commit된 파일상태와 현재 수정중인 상태 비교","type":"text"},{"type":"text","text":" "},{"type":"text","text":"git diff"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"commit된 파일상태와 add된 파일 상태 비교"},{"text":" ","type":"text"},{"type":"text","text":"git diff –staged"}]},{"inlineContent":[{"text":"commit간의 상태 비교하기 - commit hash 이용","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"git diff [비교할commit해쉬1] [비교할commit해쉬2]"},{"type":"text","text":" "},{"type":"text","text":"ex ) git diff 048171 0c747d"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"commit간의 상태 비교하기 - HEAD 이용","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"ex) git diff HEAD HEAD^"},{"type":"text","text":" "},{"text":"가장 최근의 커밋과 그 전의 커밋을 비교한다","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"branch간의 상태 비교하기 - HEAD 이용"},{"type":"text","text":" "},{"text":"git diff [비교할branch1] [비교할branch2]","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"ex ) git diff feature\/test origin\/master
local의 feature\/test브런치와 remote의 master branch 비교"}]},{"type":"thematicBreak"},{"text":"Local에서 만든 프로젝트 깃허브 저장소에 추가하는 방법","type":"heading","anchor":"Local%EC%97%90%EC%84%9C-%EB%A7%8C%EB%93%A0-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B9%83%ED%97%88%EB%B8%8C-%EC%A0%80%EC%9E%A5%EC%86%8C%EC%97%90-%EC%B6%94%EA%B0%80%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95","level":3},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"깃허브에 프로젝트 이름과 같은 이름으로 레파지토리를 생성해준다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"터미널 명령어 작성 
git remote add origin https:\/\/github.com\/leeari95\/레파지토리이름.git","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"git branch -M main
브랜치 추가하기"}]}]},{"content":[{"inlineContent":[{"text":"git push -u origin main
푸쉬하기.","type":"text"}],"type":"paragraph"}]}],"type":"orderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"text","text":"예전 버전으로 돌아가는 명령어는 총 두가지이다."}]},{"level":3,"text":"revert","type":"heading","anchor":"revert"},{"type":"paragraph","inlineContent":[{"type":"text","text":"reset이나 restore같은 명령어들은 언제든지 예전 버전으로 돌아갈 수 있지만 사실 히스토리에 예전으로 돌아갔다는 이력이 남지 않는다."},{"text":" ","type":"text"},{"type":"text","text":"해당 명령어를 이용하면 취소한 커밋의 이력이 작성되면서 커밋이 취소된다."}]},{"level":3,"anchor":"reset","text":"reset","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"내가 commit한 것이 마음에 들지 않을 때 reset을 이용하여 초기화 해줄 수 있다."},{"text":" ","type":"text"},{"type":"text","text":"특정한 커밋으로 초기화 할 수 있고, 초기화 할 때 변경했던 모든 내용들을 다시 local에 가지고 오거나 완전히 삭제하거나… 옵션이 나뉘어져 있다."}]},{"type":"thematicBreak"},{"level":3,"type":"heading","text":"branch는 무엇인가?","anchor":"branch%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%B8%EA%B0%80"},{"type":"paragraph","inlineContent":[{"type":"text","text":"가지는 안전하게 격리된 상태에서 무언가를 만들 때 사용한다."},{"type":"text","text":" "},{"text":"저장소를 만들면 기본으로 master 가지가 만들어지는데, 여기서 다른 가지(branch)를 만들어서 개발을 진행하고, 나중에 개발이 완료가 되면 master 가지로 돌아와 병합하게 된다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"git branch"},{"type":"text","text":" "},{"type":"text","text":"여기서 —all 이라는 옵션을 주게되면 서버에 있는 branch들의 모든 정보를 보여준다."}]},{"inlineContent":[{"text":"새로운 브랜치를 만들기","type":"text"},{"type":"text","text":" "},{"text":"git branch 브랜치이름","type":"text"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"브랜치를 새로만들고 바로 이동하기"},{"type":"text","text":" "},{"type":"text","text":"git checkout -b 브랜치이름"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"원하는 버전으로 이동하기"},{"type":"text","text":" "},{"text":"git checkout 해시코드 혹은 브랜치이름","type":"text"}]},{"type":"thematicBreak"},{"level":3,"type":"heading","text":"merge 병합","anchor":"merge-%EB%B3%91%ED%95%A9"},{"inlineContent":[{"type":"text","text":"마스터 브랜치에서 새로운 브랜치가 생성된 이후에 마스터 브랜치의 변동사항이 없다면"},{"text":" ","type":"text"},{"type":"text","text":"merge를 할 때 단순히 마스터 브랜치가 가리키고 있는 포인터를 옮겨놓기만 하면 된다. 그리고 브랜치를 삭제하면 깔끔하게 fast forward merges가 발생할 수 있게 된다. 그러나 fast forward는 히스토리에 merge가 되었다는 사실이 남지 않는다."},{"type":"text","text":" "},{"type":"text","text":"그래서 히스토리에 모든것이 남겨지는 것을 선호하는 스타일이라면 Three way를 사용한다. fast forward merges가 불가능한 상황이거나 merge가 된 이력이 히스토리에 남는 것을 선호한다면 three way를 사용할 수 있다."}],"type":"paragraph"},{"anchor":"merge-conflict","type":"heading","text":"merge conflict","level":3},{"inlineContent":[{"type":"text","text":"Git이 merge를 할 때 무언가 문제가 있어서 자동적으로 해결이 안 된 무언가 충돌이 났을 때 발생할 수 있다. 즉 두가지의 브랜치에서 동일한 파일을 수정했다면 Git이 도대체 ‘아 어떤 내용을 추가해야 되지?’ ‘어떤 내용을 받아들여야 되지?’ 혼동스러울 때 이제 conflict이 발생하게 된다. 이런경우에는 직접 수정을 해줘야 한다."},{"type":"text","text":" "},{"type":"image","identifier":"https:\/\/i.imgur.com\/SMIxu8v.png"},{"text":" ","type":"text"},{"type":"text","text":" "},{"text":" ","type":"text"},{"text":"파일을 직접 확인해보면 어디서에서 충돌이 일어났는지 Git이 정보를 주기위해서 문자열로 표시가 자동으로 삽입 되어있는 것을 볼 수 있다.","type":"text"},{"type":"text","text":" "},{"type":"image","identifier":"https:\/\/i.imgur.com\/US64AMI.png"}],"type":"paragraph"},{"type":"thematicBreak"},{"text":"충돌을 수동으로 해결하는 방법","type":"heading","anchor":"%EC%B6%A9%EB%8F%8C%EC%9D%84-%EC%88%98%EB%8F%99%EC%9C%BC%EB%A1%9C-%ED%95%B4%EA%B2%B0%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95","level":3},{"inlineContent":[{"type":"text","text":"open main.txt 로 파일을 열어서 직접 문자열을 수정해주면 된다."},{"type":"text","text":" "},{"type":"text","text":"주의해야될 점은 충돌만 해결해야된다는 점이다. ‘아 이왕 하는 김에 조금 다른 문장도 넣고 다른 코드도 넣고 조금 수정을 한번 해볼까’ 하고 은근슬쩍 변경사항을 넣는 것은 절대 하지말아야 한다."}],"type":"paragraph"},{"type":"thematicBreak"},{"type":"heading","level":3,"text":"merge를 취소하는 방법","anchor":"merge%EB%A5%BC-%EC%B7%A8%EC%86%8C%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95"},{"type":"paragraph","inlineContent":[{"type":"text","text":"git merge —abort"}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"text","text":"conflict을 해결했으면 다 해결했다고 알려주기 위해서 git add 를 이용해 라는걸 확인할 수 있다."},{"type":"text","text":" "},{"text":" ","type":"text"}]},{"inlineContent":[{"type":"text","text":"그리고 merge를 완료해주기 위해 —continue를 사용한다."},{"type":"text","text":" "},{"type":"image","identifier":"https:\/\/i.imgur.com\/ilHpp6z.png"},{"text":" ","type":"text"},{"identifier":"https:\/\/i.imgur.com\/Y9vPDNz.png","type":"image"}],"type":"paragraph"}],"kind":"content"}],"kind":"article","references":{"doc://AriNote/documentation/AriNote/Git":{"abstract":[{"type":"text","text":"Git 관련 지식"}],"type":"topic","title":"Git","url":"\/documentation\/arinote\/git","kind":"article","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Git"},"https://i.imgur.com/US64AMI.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/US64AMI.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/US64AMI.png","type":"image"},"https://i.imgur.com/ilHpp6z.png":{"identifier":"https:\/\/i.imgur.com\/ilHpp6z.png","alt":null,"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/ilHpp6z.png","traits":["1x","light"]}]},"https://i.imgur.com/puDkCL0.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/puDkCL0.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/puDkCL0.png","type":"image"},"https://i.imgur.com/drzPEEv.png":{"identifier":"https:\/\/i.imgur.com\/drzPEEv.png","alt":null,"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/drzPEEv.png","traits":["1x","light"]}]},"https://i.imgur.com/Y9vPDNz.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/Y9vPDNz.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/Y9vPDNz.png","type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/SMIxu8v.png":{"identifier":"https:\/\/i.imgur.com\/SMIxu8v.png","alt":null,"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/SMIxu8v.png","traits":["1x","light"]}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211005_git,-_-----_--.json b/docs/data/documentation/arinote/211005_git,-_-----_--.json new file mode 100644 index 0000000..4032fb2 --- /dev/null +++ b/docs/data/documentation/arinote/211005_git,-_-----_--.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Git"]]},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211005_Git,-_-----_--"},"metadata":{"roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}],"title":"211004 사전과제, 짝프로그래밍, Git기초"},"sections":[],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211005_git,-_-----_--"]}],"primaryContentSections":[{"content":[{"text":"TIL (Today I Learned)","level":1,"type":"heading","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"text":"10월 5일 (화)","type":"text"}]},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"text":"학습 내용","type":"heading"},{"inlineContent":[{"text":"오늘은 Git 시뮬레이션 프로젝트를 통해서 미션을 수행하였다. revert, reset을 사용할 때 merge하는 것, branch를 사용하는 방법, PR을 보내는 방법 등 Git의 기능을 사용해보는 경험을 할 수 있었다. 짝꿍과 함께 번갈아가며 push를 하는게 재미있었지만, conflict를 해결하는 부분은 어려워서 조금 헤매기도 했다. 그래도 함께 작업하는 방법에 대해서 학습할 수 있어서 유익했다. Git 시뮬레이션 프로젝트가 끝난 후에는 첫번째 팀 프로젝트를 위해 팀원들과 모여서 순서도 작성부터 시작해서 코드 작성은 어떻게 할지… 설계하기 위해 간단한 스케치만 먼저 그려놓았다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":" "}],"type":"paragraph"},{"level":2,"text":"문제점 \/ 고민한 점","type":"heading","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"혼자서 Git을 사용해본 적은 있지만 다른 사람과 함께 협업하는 경험은 처음이라서 어떻게 하면 함께 Git 기능을 익혀가면서 commit을 주고받을지 고민하였다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Git 시뮬레이션이 끝나고 아직 마무리하지 못한 짝궁을 도와주러 갔다가 PR 충돌에 대한 경험을 겪었다. 해결방안에 대해 같이 고민해보았다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"순서도 작성을 하기 위해 프로젝트 STEP들을 참고해가며 프로젝트가 어떤식으로 동작하는지 알아가는 과정이 있었는데, 머릿속으로 조건에 맞춰 어떤식으로 출력해야 하는지 생각하니 너무 복잡했다."}]}]},{"content":[{"inlineContent":[{"text":"프로젝트 팀원들과 설계하는 과정에서 프로젝트 제약사항을 나도 모르게 훑어만보고 꼼꼼히 읽지 않았다는 사실을 깨달았다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":" "}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"heading","anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","level":2,"text":"해결방법"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Git 시뮬레이션 프로젝트를 할때 서로 화면공유를 키고 서로의 터미널창을 함께 보면서 기능을 같이 익혀나갔다. 같이 명령어를 어떻게 어떤식으로 사용해야하는지 알아가면서 짜잘한 팁들도 같이 공유하게 되는 계기가 되었다."}]}]},{"content":[{"inlineContent":[{"text":"PR 충돌이 일어난다면 하단에 ","type":"text"},{"text":"Resolve conflicts","type":"text"},{"type":"text","text":" 버튼을 클릭하여 해결할 수 있다. 수정할 때 꼭 충돌 마커 "},{"text":"<<<<<<<","type":"text"},{"type":"text","text":", "},{"text":"=======","type":"text"},{"text":", ","type":"text"},{"text":">>>>>>>","type":"text"},{"text":"를 제거해줘야 ","type":"text"},{"text":"Mask as resolved","type":"text"},{"type":"text","text":" 버튼이 활성화된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"일단 순서도를 떠오르는대로 작성부터 해보았다. 그 뒤에 쭉 확인해보면서 이상해보이는 부분을 수정해가면서 완성도를 높혀나갔다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로젝트 요구사항과 제약사항을 계속 반복해가며 꼼꼼히 읽어보았다."},{"text":" ","type":"text"},{"text":" ","type":"text"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"identifier":"https:\/\/docs.github.com\/en\/github\/collaborating-with-pull-requests\/addressing-merge-conflicts\/resolving-a-merge-conflict-on-github","type":"reference"}]}]},{"content":[{"inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/app.diagrams.net\/"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"kind":"article","schemaVersion":{"major":0,"minor":3,"patch":0},"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/Git":{"abstract":[{"type":"text","text":"Git 관련 지식"}],"type":"topic","title":"Git","url":"\/documentation\/arinote\/git","kind":"article","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Git"},"https://app.diagrams.net/":{"title":"순서도 그리는 사이트","titleInlineContent":[{"type":"text","text":"순서도 그리는 사이트"}],"url":"https:\/\/app.diagrams.net\/","identifier":"https:\/\/app.diagrams.net\/","type":"link"},"https://docs.github.com/en/github/collaborating-with-pull-requests/addressing-merge-conflicts/resolving-a-merge-conflict-on-github":{"type":"link","identifier":"https:\/\/docs.github.com\/en\/github\/collaborating-with-pull-requests\/addressing-merge-conflicts\/resolving-a-merge-conflict-on-github","url":"https:\/\/docs.github.com\/en\/github\/collaborating-with-pull-requests\/addressing-merge-conflicts\/resolving-a-merge-conflict-on-github","title":"PR 충돌 해결방법","titleInlineContent":[{"type":"text","text":"PR 충돌 해결방법"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211006_------,lazy,---,----,----,-----_----_----.json b/docs/data/documentation/arinote/211006_------,lazy,---,----,----,-----_----_----.json new file mode 100644 index 0000000..0ef552f --- /dev/null +++ b/docs/data/documentation/arinote/211006_------,lazy,---,----,----,-----_----_----.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"sections":[],"metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","title":"211006 첫팀프로젝트,lazy,가독성,성능고려,재사용성,고차함수와 반복문의 성능차이"},"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211006_------,lazy,---,----,----,-----_----_----","interfaceLanguage":"swift"},"primaryContentSections":[{"kind":"content","content":[{"type":"heading","text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned","level":1},{"inlineContent":[{"text":"10월 6일 (수)","type":"text"}],"type":"paragraph"},{"type":"heading","text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"inlineContent":[{"type":"text","text":"오늘은 팀원들과 첫 프로젝트를 시작하였다. 어제 작성하였던 순서도를 팀원들과 함께 마무리 짓고, 프로젝트 STEP 1을 함께 구현해보았다. 가독성과 성능면을 고려하다가 새롭게 알게된 키워드들도 한번 훑어보게 되었는데, Swift에서 가장 신경쓰는 메모리 관련된 내용이라 깊게 공부해봐야겠다."},{"type":"text","text":" "},{"type":"text","text":" "}],"type":"paragraph"},{"type":"heading","text":"문제점 \/ 고민한 점","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"커밋을 어떤 방식으로 해야하는지 고민되었다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"변수와 함수를 구현할 때 네이밍에 대해서 팀원들과 함께 고민해보았다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"맥북 배터리가 고장나서 수리를 맡기는 바람에… 팀 프로젝트를 어떻게 진행해야 할지 막막했다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"커밋 방식은 둘째치고 커밋 단위는 기능 단위로 해야하는 것이 아닌가…?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"가독성을 위해서 단위를 쪼개가며 변수를 여러개 생성하는 것이 좋은건지 성능을 위해서 한번에 작성하는 것이 나은건지 고민되었다. 두 가지를 충족시킬 순 없을까?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"함수를 작성하면서 재사용성, 변경가능성을 고려하여 파라미터를 작성하면 좋겠다는 생각이 들었다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"제약사항 때문에 함수안에 또 다른 기능이 생긴다면 따로 함수로 빼면 좋을 것 같다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"프로젝트를 하다가 다른 의견이 나왔고, 그 의견들 중 어느 것이 좋은 건지 모르는 상황이 닥쳐왔을 때 어떻게하면 좋을까?","type":"text"},{"text":" ","type":"text"},{"type":"text","text":" "}]}]}]},{"text":"해결방법","level":2,"type":"heading","anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"팀원들과 의논하여 요구사항대로 커밋을 작성해보기로 하였으나 이후 밥먹으면서 커밋은 기능단위로 쪼개야 좋다는 것이 생각이 나서 다시한번 의논 후 기능 단위로 쪼개기로 이야기했다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"네이밍을 함께 고민해보며 제일 적절한 이름으로 네이밍을 해보았다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"아이패드로 어찌저찌 모든 것을 해결 하였다. (플레이그라운드, 크롬 등…)","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"변수의 가독성, 성능을 고민해보다가 Method Chaining이라는 키워드를 얻게 되었다. 그러나 찾아보니 Method Chaining을 하는것이 딱히 장점이 없는 것 같았다. 성능면에서도 같은 내용을 쪼개서 적는다 해도 별차이 없을 것 같다는 결론이 났다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Method Chaining을 알아보다가 고차함수와 반복문의 성능을 비교하면 어떤 것이 더 빠른지 알아보았다. 결론은 엄청 큰 데이터를 다루는 것이 아니면 성능 면에서는 큰 차이가 없기 때문에 고차함수가 적절하다는 결론이 나왔다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"프로젝트 요구사항을 충족 시키면서 함수의 재사용성을 고려하여 파라미터를 받는게 맞다는 생각으로 결론이 났다. 분명 실무에서도 재사용성을 고려하면서 코드를 작성해야될거기 때문이다. 한번 팀원들과 다시한번 이야기를 나눠봐야겠다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"어떤 의견이 좋은지 모르는 상황이 온다면 두개다 구현해보고 리뷰어에게 물어보는 것이 방법이 될 것 같다!","type":"text"},{"text":" ","type":"text"},{"type":"text","text":" "}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"type":"reference","identifier":"http:\/\/minsone.github.io\/mac\/ios\/method-chaining-in-swift"}]}]},{"content":[{"inlineContent":[{"isActive":true,"identifier":"https:\/\/seons-dev.tistory.com\/126","type":"reference"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/stackoverflow.com\/questions\/50433122\/whats-the-performance-of-chaining-swifts-array-higher-order-functions","type":"reference","isActive":true}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","isActive":true,"identifier":"https:\/\/www.skoumal.com\/en\/performance-of-built-in-higher-order-functions-map-filter-reduce-and-flatmap-vs-for-in-loop-in-swift\/"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211006_------,lazy,---,----,----,-----_----_----"]}],"schemaVersion":{"minor":3,"patch":0,"major":0},"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://seons-dev.tistory.com/126":{"titleInlineContent":[{"text":"lazy에 대해 알아보자","type":"text"}],"identifier":"https:\/\/seons-dev.tistory.com\/126","url":"https:\/\/seons-dev.tistory.com\/126","type":"link","title":"lazy에 대해 알아보자"},"http://minsone.github.io/mac/ios/method-chaining-in-swift":{"title":"Method Chaining은 무엇일까?","type":"link","titleInlineContent":[{"text":"Method Chaining은 무엇일까?","type":"text"}],"url":"http:\/\/minsone.github.io\/mac\/ios\/method-chaining-in-swift","identifier":"http:\/\/minsone.github.io\/mac\/ios\/method-chaining-in-swift"},"https://stackoverflow.com/questions/50433122/whats-the-performance-of-chaining-swifts-array-higher-order-functions":{"titleInlineContent":[{"text":"고차함수 성능에 대한 stackoverflow","type":"text"}],"identifier":"https:\/\/stackoverflow.com\/questions\/50433122\/whats-the-performance-of-chaining-swifts-array-higher-order-functions","url":"https:\/\/stackoverflow.com\/questions\/50433122\/whats-the-performance-of-chaining-swifts-array-higher-order-functions","type":"link","title":"고차함수 성능에 대한 stackoverflow"},"https://www.skoumal.com/en/performance-of-built-in-higher-order-functions-map-filter-reduce-and-flatmap-vs-for-in-loop-in-swift/":{"title":"고차함수와 반복문의 비교","type":"link","titleInlineContent":[{"text":"고차함수와 반복문의 비교","type":"text"}],"url":"https:\/\/www.skoumal.com\/en\/performance-of-built-in-higher-order-functions-map-filter-reduce-and-flatmap-vs-for-in-loop-in-swift\/","identifier":"https:\/\/www.skoumal.com\/en\/performance-of-built-in-higher-order-functions-map-filter-reduce-and-flatmap-vs-for-in-loop-in-swift\/"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211007_----,---,kvo,compactmap,----.json b/docs/data/documentation/arinote/211007_----,---,kvo,compactmap,----.json new file mode 100644 index 0000000..c7e81ba --- /dev/null +++ b/docs/data/documentation/arinote/211007_----,---,kvo,compactmap,----.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"kind":"content","content":[{"level":1,"text":"TIL (Today I Learned)","type":"heading","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"text":"10월 7일 (수)","type":"text"}]},{"text":"학습 내용","type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"paragraph","inlineContent":[{"type":"text","text":"오늘은 오전에 땅따먹기 게임을 해보았다. 두 단어를 한 문장으로 설명하면서 땅따먹는 룰이였는데, 익숙한 용어들이였지만 말로 설명하려니까 쉽지않았다. 머리로는 이해하고 있는 지식들이였지만 설명하려고 하니 좀처럼 입이 떨어지지가 않았다. 그래도 팀원들과 같이 설명해보면서 모르는 부분도 함께 토론해보며 평소에는 알고있지 않던 지식을 얻을 수 있었던 유익한 시간이였다. 이후 어제 진행하였던 팀 프로젝트를 수정한 뒤 STEP2를 진행하였다."},{"type":"text","text":" "},{"type":"text","text":" "}]},{"text":"문제점 \/ 고민한 점","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","type":"heading","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"싱글톤","type":"text"},{"type":"text","text":", "},{"text":"KVO","type":"text"},{"text":" 라는 용어는 처음보는 용어였는데, 팀원이 설명할 때 정확히 맞는 설명인건지 알고있는 지식이 아니라서 확신이 없었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"내가 입으로 설명할 수 없으면 알고있는 지식이 아니라는 걸 새삼 깨달았다."}]}]},{"content":[{"inlineContent":[{"text":"함수는 타입의 일종인걸까?","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로토콜은 일급 시민이였나?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로젝트 진행 중에 "},{"type":"text","text":"String 배열"},{"text":"을 ","type":"text"},{"type":"text","text":"Int 배열"},{"text":"로 변환하는 과정에서 map을 사용하니 옵셔널 형태로 반환되어서 일반 정수배열로 반환할 수 없는지 고민하였다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"인수 레이블 이름 지정하는 것을 고민하였으나 파라미터가 많을 경우라 그런지 쉽지 않았었다.","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"text":"해결방법","type":"heading","level":2,"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"구글링 해보면서 내가 이해하고 있는게 맞는지 팀원들에게 확인해보았는데, 다들 모르는 지식이였어서 다같이 찾아보면서 입으로 설명해보고 점차 이해를 넓혀갔다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"KVO : ","type":"text"},{"text":"Key-Value Observing","type":"text"},{"text":"의 약자이다. 객체의 프로퍼티의 변경사항을 다른 객체에 알리기 위해 사용하는 코코아 프로그래밍 패턴이다. Model과 View와 같이 논리적으로 분리된 파크간의 변경사항을 전달하는데 유용하고 ","type":"text"},{"text":"NSObjec","type":"text"},{"text":"를 상속한 클래스에서만 KVO를 사용할 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"싱글톤 : 특정 용도로 객체를 하나만 생성하여 공용으로 사용하고 싶을 때 사용하는 디자인 유형이다. init을 ","type":"text"},{"text":"private","type":"text"},{"type":"text","text":"로 접근제어하고 타입 내부에서 "},{"text":"static","type":"text"},{"type":"text","text":"을 이용하여 인스턴스를 저장할 프로퍼티를 생성한다. 여기서 인스턴스는 참조해야하기 때문에 "},{"text":"class","type":"text"},{"type":"text","text":"에서만 사용할 수 있는 패턴이다. 접근은 생성한 "},{"type":"text","text":"static"},{"text":" 프로퍼티를 이용하면 된다. 아래는 싱글톤 패턴을 사용한 타입 FileManager의 사용법이다.","type":"text"}]}]}]},{"type":"codeListing","syntax":"swift","code":["\/\/ 파일매니저 인스턴스 생성","let fileManager = FileManager.default ","\/\/ 사용자의 문서 경로","let documentPath: URL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]"]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"앞으로 꼭 팀원에게 설명할 기회가 있다면 입으로 설명해보는 ","type":"text"},{"type":"strong","inlineContent":[{"type":"text","text":"설명충"}]},{"type":"text","text":"이 되어야겠다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"함수는 이름이 없는 객체이다. 일급시민, 즉 값으로 다룰 수 있는 요소이기 때문에 타입과는 다르다는 것으로 결론이 났다. 타입은 이름이 있는 타입(","type":"text"},{"type":"text","text":"class, struct, enum, protocol 등"},{"type":"text","text":")이 있고, 이름이 없는 타입("},{"text":"tuple, closure, func","type":"text"},{"type":"text","text":")이 있다."}]}]},{"content":[{"inlineContent":[{"text":"프로토콜은 일급시민에 해당되지 않는다. 프로토콜은 정의만 해줄 뿐, 프로퍼티에 기본값을 줄 수는 없다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"compactMap이라는 고차함수를 사용해보았다. 공식문서를 보니 nil을 알아서 걸러주고 nil이 아닌 값들만 추려서 반환해준다고 설명이 되어있었다. 따라서 ","type":"text"},{"type":"text","text":"nil"},{"type":"text","text":"을 제거하고, 주어진 값을 "},{"text":"옵셔널 바인딩","type":"text"},{"type":"text","text":" 할때 유용하게 사용할 수 있을 것 같다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"함수 호출 시 한 문장으로 설명된다면 성공적인 인수레이블을 지을 수 있을 것 같다. Swift 기본 함수들의 인수 레이블을 참고하여 한 문장으로 읽히도록 노력해봐야겠다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":" "}]}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/docs.swift.org\/swift-book\/ReferenceManual\/Types.html","isActive":true}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/zeddios.tistory.com\/1220","isActive":true}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/babbab2.tistory.com\/66","isActive":true}]}]},{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/swift\/sequence\/2950916-compactmap","isActive":true}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"sections":[],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211007_----,---,KVO,compactMap,----"},"metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"title":"211007 땅따먹기,싱글톤,KVO,compactMap,프로젝트","roleHeading":"Article"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"kind":"article","schemaVersion":{"patch":0,"minor":3,"major":0},"variants":[{"paths":["\/documentation\/arinote\/211007_----,---,kvo,compactmap,----"],"traits":[{"interfaceLanguage":"swift"}]}],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://babbab2.tistory.com/66":{"titleInlineContent":[{"type":"text","text":"싱글톤 패턴에 대하여…"}],"url":"https:\/\/babbab2.tistory.com\/66","identifier":"https:\/\/babbab2.tistory.com\/66","title":"싱글톤 패턴에 대하여…","type":"link"},"https://zeddios.tistory.com/1220":{"titleInlineContent":[{"type":"text","text":"KVO란 무엇인가?"}],"url":"https:\/\/zeddios.tistory.com\/1220","identifier":"https:\/\/zeddios.tistory.com\/1220","title":"KVO란 무엇인가?","type":"link"},"https://developer.apple.com/documentation/swift/sequence/2950916-compactmap":{"titleInlineContent":[{"type":"text","text":"compactMap"}],"url":"https:\/\/developer.apple.com\/documentation\/swift\/sequence\/2950916-compactmap","identifier":"https:\/\/developer.apple.com\/documentation\/swift\/sequence\/2950916-compactmap","title":"compactMap","type":"link"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://docs.swift.org/swift-book/ReferenceManual/Types.html":{"titleInlineContent":[{"type":"text","text":"Swift Type"}],"url":"https:\/\/docs.swift.org\/swift-book\/ReferenceManual\/Types.html","identifier":"https:\/\/docs.swift.org\/swift-book\/ReferenceManual\/Types.html","title":"Swift Type","type":"link"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211008_destructuring,_commit,_branch,_changes_requested,_refactoring,_-----_----_--,_naming,_reduce.json b/docs/data/documentation/arinote/211008_destructuring,_commit,_branch,_changes_requested,_refactoring,_-----_----_--,_naming,_reduce.json new file mode 100644 index 0000000..14f6c93 --- /dev/null +++ b/docs/data/documentation/arinote/211008_destructuring,_commit,_branch,_changes_requested,_refactoring,_-----_----_--,_naming,_reduce.json @@ -0,0 +1 @@ +{"sections":[],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211008_destructuring,_commit,_branch,_changes_requested,_refactoring,_-----_----_--,_naming,_reduce"]}],"schemaVersion":{"patch":0,"minor":3,"major":0},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211008_Destructuring,_commit,_branch,_Changes_requested,_refactoring,_-----_----_--,_naming,_reduce","interfaceLanguage":"swift"},"primaryContentSections":[{"content":[{"type":"heading","anchor":"TIL-Today-I-Learned","level":1,"text":"TIL (Today I Learned)"},{"inlineContent":[{"text":"10월 8일 (금)","type":"text"}],"type":"paragraph"},{"level":2,"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"inlineContent":[{"text":"자정부터 잠안자고 피드백을 살펴보면서 고민해보는 시간을 가지다가 새벽 2시쯤… 잠이들었다. 이후 오전 아침에는 활동학습으로 옵셔널에 대해서 예제코드를 작성해본다음 서로 설명해보는 시간을 가졌다. 옵셔널을 거의 이해하고 있다고 생각했는데 알고 있지 못했던 생소한 것들도 얻게되는 유익한 시간이였다. 도중에 예약시간이 다되서 백신 2차접종을 맞으러갔다. 오후에는 타이레놀로 버텨가며 팀원들과 피드백에 대한 고민을 시작해보기로 하였다. 프로젝트 중간에 리뷰어랑 서포터즈가 회의실에 들어와서 많은 것들을 피드백해주며 가르침을 주셨다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":" "}],"type":"paragraph"},{"anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","text":"문제점 \/ 고민한 점","level":2,"type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Swift의 기본 타입들의 코드 내부를 보려면 어떻게 해야하지?","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"들여쓰기는 함수 포함하여 카운트하는 것이 아닌가..?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"naming"},{"type":"text","text":"이 가장 어려운 문제인 것 같다. 적절하게 하려면 어떤 것이 중요할까?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Int 배열"},{"text":"을 ","type":"text"},{"type":"codeVoice","code":"joined"},{"type":"text","text":" 해보라는 피드백에서 그럼 "},{"type":"codeVoice","code":"map"},{"type":"text","text":"으로 "},{"code":"String 배열","type":"codeVoice"},{"type":"text","text":"로 변환후 "},{"code":"joined","type":"codeVoice"},{"type":"text","text":"를 해야되겠다… 하다가, "},{"code":"reduce","type":"codeVoice"},{"type":"text","text":"로 합칠 수 있는 것이 아닌가? 라는 의문이 들었다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"튜플을 선언하면서 값들을 바로 불러올 수 있는 방법이 뭐가 있을까?"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"전역변수"},{"type":"text","text":"를 잘 안쓰게 되는 이유는 뭘까?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"프로젝트 요구사항에 대해서 ","type":"text"},{"inlineContent":[{"type":"text","text":"의문점"}],"type":"emphasis"},{"type":"text","text":" 이 생긴다면 어떻게 해야할까?"}]}]},{"content":[{"inlineContent":[{"code":"commit","type":"codeVoice"},{"text":"시 ","type":"text"},{"type":"codeVoice","code":"body"},{"type":"text","text":"를 적는 방법을 모르겠다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"refactoring"},{"type":"text","text":"의 스킬들이 궁금해졌다. 그리고 그 방식들이 일반적인 방법보다 "},{"inlineContent":[{"text":"왜?","type":"text"}],"type":"emphasis"},{"type":"text","text":" 좋은지 더 공부해보고 싶다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"함수는 무조건 짧은 것이 좋을까? 아니면 몇줄 길어도 가독성이 좋은 내부가 더 좋은걸까?","type":"text"}]}]},{"content":[{"inlineContent":[{"code":"master 브랜치","type":"codeVoice"},{"text":"가 ","type":"text"},{"type":"codeVoice","code":"기능 구현을 위해 별도로 만들어둔 브랜치"},{"text":"보다 앞서 나가있는 상황에서 ","type":"text"},{"code":"merge","type":"codeVoice"},{"text":"해야하는 상황이라면 어떤식으로 ","type":"text"},{"code":"merge","type":"codeVoice"},{"text":"를 해나가야 할까?","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Changes requested"},{"text":" 이후 재검토 요청을 하려면 어떻게 해야하지?","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"순서도 작성 기준에 대해서… 간단히 적어도 될까?","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"REDME는 어떤식으로 작성해나가야 하지?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"프로젝트 STEP을 모두 마무리하지 못해서 아쉬웠다. 끝까지 마무리 하고 싶었지만, 여기까지여도 배운 것이 정말 많기 때문에 마무리 짓기로 하였다.","type":"text"},{"type":"text","text":" "},{"text":" ","type":"text"}]}]}],"type":"unorderedList"},{"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","level":2,"text":"해결방법","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"Google"}],"type":"strong"},{"text":"에 ","type":"text"},{"type":"codeVoice","code":"swift github optional operator"},{"type":"text","text":"라고 검색하니 바로 맨 상단에 나타났다. 코드 내부를 보고싶을 때에는 "},{"type":"codeVoice","code":"Swift Github"},{"type":"text","text":"를 찾아가면 될 것 같다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"들여쓰기 기준은 함수를 포함하여 카운트 하는 것이 맞는 것 같지만, 사람마다 의견차이가 있는 것 같다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"naming","type":"codeVoice"},{"text":"은 서포터즈 ","type":"text"},{"type":"codeVoice","code":"Wody"},{"type":"text","text":"에게 조언을 구했다. "},{"type":"codeVoice","code":"naming"},{"text":"시에는 함수나 변수의 역할에 집중하며, 최대한 하나의 역할을 맡는 이름을 지어주면 될 것 같다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"reduce로 아주 손쉽게 Int배열을 합쳐줄 수가 있었다.","type":"text"}]},{"type":"codeListing","syntax":"swift","code":["\/\/ 예제","let numbers = [1, 2, 3]","print(numbers.reduce(\"\", { $0 + String($1) + \" \" }))","\/\/ \"1 2 3 \""]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"destructuring에 대해서 알게되었다. 튜플을 단일 할당할 때 여러값으로 분리하는 방법이다.","type":"text"}]},{"code":["let (first, last) = (\"Ari\", \"Lee\")","print(first) \/\/ Ari","print(last) \/\/ Lee"],"syntax":"swift","type":"codeListing"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"전역변수를 사용하면 생기는 문제","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"어디서 값이 변경되는지 추적이 어렵다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"프로그램 내 변수와 함수 등 여러 요소들과 간섭이 심해짐","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"전역 변수는 모든 곳에서 보이므로 네임스페임스가 오염된다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"전역 변수가 메모리 할당 외에 side effect가 있으면 문제 발생 소지가 크다. (상호 의존 등…)","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"전역 변수는 생명 주기가 길다. 따라서 메모리 리소스도 오랜 기간 소비한다.(메모리릭)"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로젝트 요구사항에 대해서 "},{"code":"\"이 부분은 고려해야하는 것이 아닌가?\"","type":"codeVoice"},{"type":"text","text":"하는 생각이 들 때면 "},{"type":"codeVoice","code":"요구사항을 제시한 사람"},{"text":"에게 찾아가 물어보는 것이 ","type":"text"},{"type":"strong","inlineContent":[{"text":"가장 정확한 답","type":"text"}]},{"type":"text","text":"이 될 수 있겠다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"commit"},{"text":"시 ","type":"text"},{"code":"body","type":"codeVoice"},{"type":"text","text":"를 적을 때에는 한번 "},{"code":"줄바꿈","type":"codeVoice"},{"type":"text","text":" 하여 작성해야 하며, "},{"type":"codeVoice","code":"명령형 어조"},{"type":"text","text":", "},{"type":"codeVoice","code":"현재 시제"},{"type":"text","text":"를 사용하는 것을 권장한다. 또한 이전 행동과 대조하여 "},{"code":"변경을 한 동기","type":"codeVoice"},{"text":"를 포함하는 것을 권장한다. 보기좋은 메세지를 만들고 싶다면 ","type":"text"},{"code":"-","type":"codeVoice"},{"text":"와 ","type":"text"},{"code":"줄바꿈","type":"codeVoice"},{"type":"text","text":"을 활용하여 작성한다."},{"type":"image","identifier":"https:\/\/i.imgur.com\/eygw8Tv.png"}]}]},{"content":[{"inlineContent":[{"text":"refactoring 스킬들","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"하나의 함수에서 너무 많은 일을 하고 있지 않은지 확인한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"새로운 타입을 만들어 활용한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"구현체를 직접적으로 의존하지말고, 추상화한 상위 클래스 또는 프로토콜을 의존하도록 설계한다."}]}]},{"content":[{"inlineContent":[{"text":"객체의 내부 프로퍼티를 보호하고, 인터페이스를 추상화한다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"내가 생각했을 때는 함수의 길이가 너무 길어지는 것도 안좋지만 그렇다고 조금 길어지는 정도 가지고 분리하면 안되는 부분을 따로 분리하여 함수를 만들어도 가독성이 떨어지는 것 같다. 로직을 변경해보는 다른 방법도 한번 고려해보는 것이 좋을 것 같다는 생각이 들었다. 항상 어려운 상황이 닥치게 되면 피하려고만 했는데 그러지말고 일단 발생한 문제에 맞서서 다르게 고민해봐야겠다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"먼저 "},{"type":"codeVoice","code":"앞서나간 브랜치(master)"},{"text":"를 ","type":"text"},{"type":"codeVoice","code":"뒤쳐져있는 브랜치"},{"text":"에 ","type":"text"},{"type":"codeVoice","code":"merge"},{"text":"하고 ","type":"text"},{"code":"conflict","type":"codeVoice"},{"text":"을 해결한다. 이후 다시 ","type":"text"},{"code":"master branch","type":"codeVoice"},{"type":"text","text":"에 "},{"type":"codeVoice","code":"merge"},{"type":"text","text":"하여 합치면 된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"순서도를 작성하는 이유는 ","type":"text"},{"type":"codeVoice","code":"설계"},{"type":"text","text":"를 위한 것이다. 따라서 코드를 작성하기 전에 어떻게 설계를 할지 충분히 고민한 후 설계도를 작성하는 것이 좋겠다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"REDME는 구현사항을 간단하게 적고, ","type":"text"},{"code":"고민했던 것들","type":"codeVoice"},{"text":", ","type":"text"},{"code":"궁금했던 것들","type":"codeVoice"},{"type":"text","text":", 그리고 "},{"type":"codeVoice","code":"Trouble Shooting"},{"type":"text","text":" 위주로 잘 정하면 될 것 같다."}]}]},{"content":[{"inlineContent":[{"text":"함수의 순서는 읽기 좋은 순서대로 하는 것을 권장한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"프로젝트는 모든 STEP을 마치는 것이 중요한게 아니라 STEP을 진행하면서 해왔던 "},{"code":"고민들","type":"codeVoice"},{"type":"text","text":"과 "},{"code":"경험","type":"codeVoice"},{"text":"들이 ","type":"text"},{"inlineContent":[{"type":"text","text":"중요하다."}],"type":"strong"},{"type":"text","text":" "},{"text":" ","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/www.hackingwithswift.com\/example-code\/language\/what-is-destructuring","isActive":true}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/seizze.github.io\/2020\/03\/21\/%EC%8A%A4%EC%9C%84%ED%94%84%ED%8A%B8-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%8A%A4%ED%82%AC%EB%93%A4.html","isActive":true}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/www.it-gundan.com\/ko\/git\/github%EC%97%90%EC%84%9C-%ED%92%80-%EC%9A%94%EC%B2%AD%EC%9D%84-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%ED%95%9C-%ED%9B%84-%EA%B2%80%ED%86%A0-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EB%A5%BC-%EC%9E%AC%EA%B0%9C%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C\/829139036\/","type":"reference","isActive":true}]}]},{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/github.com\/apple\/swift\/blob\/main\/stdlib\/public\/core\/Optional.swift","isActive":true}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/int\/3127690-zero","isActive":true,"type":"reference"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}],"kind":"content"}],"metadata":{"title":"211008 Destructuring, commit, branch, Changes requested, refactoring, 전역변수를 잘안쓰는 이유, naming, reduce","modules":[{"name":"Ari's Notes"}],"role":"article","roleHeading":"Article"},"references":{"https://i.imgur.com/eygw8Tv.png":{"alt":null,"identifier":"https:\/\/i.imgur.com\/eygw8Tv.png","variants":[{"url":"https:\/\/i.imgur.com\/eygw8Tv.png","traits":["1x","light"]}],"type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://seizze.github.io/2020/03/21/%EC%8A%A4%EC%9C%84%ED%94%84%ED%8A%B8-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%8A%A4%ED%82%AC%EB%93%A4.html":{"identifier":"https:\/\/seizze.github.io\/2020\/03\/21\/%EC%8A%A4%EC%9C%84%ED%94%84%ED%8A%B8-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%8A%A4%ED%82%AC%EB%93%A4.html","title":"refactoring 스킬들","type":"link","titleInlineContent":[{"text":"refactoring 스킬들","type":"text"}],"url":"https:\/\/seizze.github.io\/2020\/03\/21\/%EC%8A%A4%EC%9C%84%ED%94%84%ED%8A%B8-%EC%BD%94%EB%93%9C-%EB%A6%AC%ED%8C%A9%ED%86%A0%EB%A7%81-%EC%8A%A4%ED%82%AC%EB%93%A4.html"},"https://github.com/apple/swift/blob/main/stdlib/public/core/Optional.swift":{"identifier":"https:\/\/github.com\/apple\/swift\/blob\/main\/stdlib\/public\/core\/Optional.swift","title":"Optional 코드 내부 들여다보기","type":"link","titleInlineContent":[{"type":"text","text":"Optional 코드 내부 들여다보기"}],"url":"https:\/\/github.com\/apple\/swift\/blob\/main\/stdlib\/public\/core\/Optional.swift"},"https://www.hackingwithswift.com/example-code/language/what-is-destructuring":{"identifier":"https:\/\/www.hackingwithswift.com\/example-code\/language\/what-is-destructuring","title":"Destructuring이란?","type":"link","titleInlineContent":[{"text":"Destructuring이란?","type":"text"}],"url":"https:\/\/www.hackingwithswift.com\/example-code\/language\/what-is-destructuring"},"https://developer.apple.com/documentation/swift/int/3127690-zero":{"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/int\/3127690-zero","title":"Int.zero","type":"link","titleInlineContent":[{"type":"text","text":"Int.zero"}],"url":"https:\/\/developer.apple.com\/documentation\/swift\/int\/3127690-zero"},"https://www.it-gundan.com/ko/git/github%EC%97%90%EC%84%9C-%ED%92%80-%EC%9A%94%EC%B2%AD%EC%9D%84-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%ED%95%9C-%ED%9B%84-%EA%B2%80%ED%86%A0-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EB%A5%BC-%EC%9E%AC%EA%B0%9C%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C/829139036/":{"identifier":"https:\/\/www.it-gundan.com\/ko\/git\/github%EC%97%90%EC%84%9C-%ED%92%80-%EC%9A%94%EC%B2%AD%EC%9D%84-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%ED%95%9C-%ED%9B%84-%EA%B2%80%ED%86%A0-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EB%A5%BC-%EC%9E%AC%EA%B0%9C%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C\/829139036\/","title":"Changes requested 이후 재검토요청하는 방법","type":"link","titleInlineContent":[{"text":"Changes requested 이후 재검토요청하는 방법","type":"text"}],"url":"https:\/\/www.it-gundan.com\/ko\/git\/github%EC%97%90%EC%84%9C-%ED%92%80-%EC%9A%94%EC%B2%AD%EC%9D%84-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8-%ED%95%9C-%ED%9B%84-%EA%B2%80%ED%86%A0-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EB%A5%BC-%EC%9E%AC%EA%B0%9C%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9E%85%EB%8B%88%EA%B9%8C\/829139036\/"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211009_optional,_dictionary,_compactmap.json b/docs/data/documentation/arinote/211009_optional,_dictionary,_compactmap.json new file mode 100644 index 0000000..b08214a --- /dev/null +++ b/docs/data/documentation/arinote/211009_optional,_dictionary,_compactmap.json @@ -0,0 +1 @@ +{"sections":[],"kind":"article","variants":[{"paths":["\/documentation\/arinote\/211009_optional,_dictionary,_compactmap"],"traits":[{"interfaceLanguage":"swift"}]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"schemaVersion":{"minor":3,"patch":0,"major":0},"primaryContentSections":[{"content":[{"type":"heading","text":"TIL (Today I Learned)","level":1,"anchor":"TIL-Today-I-Learned"},{"inlineContent":[{"text":"10월 8일 (금)","type":"text"}],"type":"paragraph"},{"text":"학습 내용","type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"inlineContent":[{"type":"text","text":"오늘은 불타는 토요스터디를 진행하였다. 옵셔널과 예외처리에 대해서 복습하는 시간을 가졌다. 스터디원들과 서포터즈와 간단한 근황토크와 모각코를 진행하였다. 옵셔널 바인딩을 해야하는 여러가지의 상황들을 구현해보면서 코드를 나누는 시간을 가졌다."},{"text":" ","type":"text"},{"type":"text","text":" "}],"type":"paragraph"},{"anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","text":"문제점 \/ 고민한 점","level":2,"type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Optional인 값을 switch로 어떻게 바인딩 할까?","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"compactMap으로 옵셔널바인딩을 할 수 있을 것 같다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Dictionary의 init를 다시 복습해보았는데 기억이 나질 않아…","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"indices 활용법을 잊어먹었다."},{"text":" ","type":"text"},{"text":" ","type":"text"}]}]}]},{"text":"해결방법","level":2,"type":"heading","anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"switch로 바인딩을 하려면 옵셔널인 값을 switch문으로 아래와 같이 구성하면된다."}],"type":"paragraph"},{"code":["let numbers: [String?] = [\"1\", \"2\", \"3\", \"4\", \"5\"]","","for index in numbers.indices {"," switch numbers[index] {"," case .none:"," continue"," case .some(let number):"," print(\"숫자 \\(number)을 바인딩 하는데 성공!\")"," }","}"],"type":"codeListing","syntax":"swift"}]},{"content":[{"inlineContent":[{"text":"compactMap을 활용하여 옵셔널을 벗겨보았다.","type":"text"}],"type":"paragraph"},{"type":"codeListing","code":["let numbers: [String?] = [\"1\", \"2\", \"3\", \"4\", \"5\"]","","let numbersBinding: [String] = numbers.compactMap { $0 }","let numbersTagList: Dictionary = Dictionary(uniqueKeysWithValues: zip(numbersBinding, 6...10))","let numbersSorted = numbersTagList.sorted { $0.value < $1.value }","numbersSorted.forEach{ (str, number) in"," print(\"\\(number)번째에는 숫자 \\(str)이 들어있다\")","}"],"syntax":"swift"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/optional","type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/collection\/1641719-indices","isActive":true,"type":"reference"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/github.com\/apple\/swift\/blob\/main\/stdlib\/public\/core\/FlatMap.swift","isActive":true,"type":"reference"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211009_Optional,_Dictionary,_compactMap"},"metadata":{"roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}],"title":"211009 Optional, Dictionary, compactMap"},"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://developer.apple.com/documentation/swift/optional":{"url":"https:\/\/developer.apple.com\/documentation\/swift\/optional","title":"Optional","titleInlineContent":[{"text":"Optional","type":"text"}],"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/optional","type":"link"},"https://github.com/apple/swift/blob/main/stdlib/public/core/FlatMap.swift":{"url":"https:\/\/github.com\/apple\/swift\/blob\/main\/stdlib\/public\/core\/FlatMap.swift","title":"compactMap","titleInlineContent":[{"type":"text","text":"compactMap"}],"identifier":"https:\/\/github.com\/apple\/swift\/blob\/main\/stdlib\/public\/core\/FlatMap.swift","type":"link"},"https://developer.apple.com/documentation/swift/collection/1641719-indices":{"url":"https:\/\/developer.apple.com\/documentation\/swift\/collection\/1641719-indices","title":"indices","titleInlineContent":[{"text":"indices","type":"text"}],"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/collection\/1641719-indices","type":"link"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211011_condition,_condition-list,_flow_chart,_------------,_ground_rules,_daily_scrum,_----.json b/docs/data/documentation/arinote/211011_condition,_condition-list,_flow_chart,_------------,_ground_rules,_daily_scrum,_----.json new file mode 100644 index 0000000..94a8f43 --- /dev/null +++ b/docs/data/documentation/arinote/211011_condition,_condition-list,_flow_chart,_------------,_ground_rules,_daily_scrum,_----.json @@ -0,0 +1 @@ +{"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211011_condition,_condition-list,_flow_chart,_------------,_Ground_rules,_Daily_Scrum,_----","interfaceLanguage":"swift"},"variants":[{"paths":["\/documentation\/arinote\/211011_condition,_condition-list,_flow_chart,_------------,_ground_rules,_daily_scrum,_----"],"traits":[{"interfaceLanguage":"swift"}]}],"primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","type":"heading","level":1,"text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"type":"text","text":"10월 11일 (월)"}]},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading","level":2,"text":"학습 내용"},{"type":"paragraph","inlineContent":[{"type":"text","text":"오늘은 활동학습으로 타입을 설계할 때 "},{"type":"codeVoice","code":"일반화, 추상화, 캡슐화, 은닉화"},{"text":" 를 어떤식으로 하면 좋을 지 학습해보았다. 알고있던 개념이였지만 ","type":"text"},{"inlineContent":[{"type":"text","text":"역시나"}],"type":"strong"},{"type":"text","text":" 언제나처럼 마치 처음 접하는 개념마냥 새롭게 다가왔고, 팀원들과 함께 타입을 설계하는 법에 대해서 배워보는 유익한 시간을 보냈다. 이후 새로운 팀프로젝트 팀원인 "},{"code":"알라딘","type":"codeVoice"},{"text":"과 함께 그라운드 룰을 정하고 프로젝트를 진행하기 시작하였다.","type":"text"}]},{"type":"heading","level":2,"text":"문제점 \/ 고민한 점","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"code":"일반화, 추상화, 캡슐화, 은닉화","type":"codeVoice"},{"type":"text","text":"의 개념을 정확하게 모르겠네…"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"크루, 야곰이 ","type":"text"},{"code":"Ground rules","type":"codeVoice"},{"type":"text","text":"과 "},{"code":"Daily Scrum","type":"codeVoice"},{"type":"text","text":"을 강조하네? 이유가 뭘까?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"순서도를 세세히 꼼꼼하게 적는게 정말 좋은걸까? 가독성이 떨어지진 않을까?","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"메소드명을 지을 때 파라미터명과 연결해서 지어주는 것이 나은건가? 아니면 한꺼번에 지어주는 것이 알맞을까?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"메소드 내부에 있는 "},{"type":"codeVoice","code":"readLine()"},{"text":"을 파라미터 받고, 메소드 내부로 넘겼을때 왜 커멘트 라인이 작동을 안할까?","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":", &&"},{"type":"text","text":" AND연산자와 쉼표의 쓰임새가 정말 같은걸까? 쉼표를 쓸때 나는 에러는 왜나는거지?"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"heading","level":2,"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","text":"해결방법"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"예전에 메모해둔 것을 찾았다…!!! 타입을 설계할 때 꼭 다시 읽어봐야지!"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"일반화(Generalization)","type":"text"}],"type":"strong"},{"text":" 인스턴스의 공통 특징을 뽑아내는 것","type":"text"}]}]},{"content":[{"inlineContent":[{"inlineContent":[{"type":"text","text":"추상화(Abstraction)"}],"type":"strong"},{"type":"text","text":" 공통 특성 중 관심이 있는 부분만 추출하고 나머지는 무시하는 과정"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"은닉화(Hiding)"}]},{"type":"text","text":" 주요 사항이 겉으로 드러나지 않도록 감추는 것"}]}]},{"content":[{"inlineContent":[{"inlineContent":[{"text":"캡슐화(Encapsulation)","type":"text"}],"type":"strong"},{"type":"text","text":" 중요사항을 감춘 상태에서 외부에서 그것을 사용할 수 있는 방법을 제공하고 외부와 소통을 하는 것"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"확실히 "},{"code":"Ground rules","type":"codeVoice"},{"type":"text","text":"을 정하고 프로젝트를 진행하니 훨씬 마음도 편하고, 스케줄 관리가 되는 느낌이라 쾌적한[?] 프로젝트를 진행할 수가 있었다..! 아침에 "},{"type":"codeVoice","code":"Daily Scrum"},{"type":"text","text":"도 진행해보기로 하였다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"순서도를 그리다가 너무 세세한 부분은 가독성이 떨어지는 것 같아 "},{"type":"codeVoice","code":"Steven"},{"type":"text","text":"에게 조언을 구했다. 세세한 부분은 "},{"type":"codeVoice","code":"따로 떼어내서 작성"},{"type":"text","text":"하는 것이 좀 더 가독성이 좋다는 생각을 듣고 반영해보았는데 훨씬 순서도가 보기 편하게 바뀌었다. 그리고 세세하게 그려보면서 로직을 어떻게 구현해야할 지 감도 잡히고, 프로젝트 진행 중간에 헷갈리면 정리하는 느낌으로 순서도를 유용하게 활용했다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"메소드명을 짓다가 API 문서를 보아도 헷갈리는 부분이 있었다. 알라딘과 의견을 나누어보았으나 확신이 서질 않아서 리뷰어인 "},{"code":"Soll","type":"codeVoice"},{"text":"에게 의견을 물어보았는데, 따로 메소드명+파라미터명을 연결해서 지어보는 것이 좀더 좋아보인다고 의견을 주셨다. 나중에 이 부분은 PR을 보낼 때 한번 더 상세히 물어봐야겠다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"readLine()"},{"text":" 역시 항상 질문환영 방에 들어가계시는 ","type":"text"},{"code":"Steven","type":"codeVoice"},{"text":"에게 여쭤보았다. 코드를 자세히 살펴보니 코드 ","type":"text"},{"type":"codeVoice","code":"순서"},{"type":"text","text":"의 문제였다. 순서만 해결해준다면 "},{"type":"codeVoice","code":"readLine()"},{"text":"도 파라미터로 받아서 사용할 수 있을 것 같다! 내일 알라딘과 이야기 해봐야지.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":",","type":"codeVoice"},{"text":"는 ","type":"text"},{"type":"codeVoice","code":"condition"},{"type":"text","text":"을 "},{"code":"이어붙이는 용도","type":"codeVoice"},{"type":"text","text":"로 쓰는 것이고, "},{"code":"&&","type":"codeVoice"},{"type":"text","text":"는 "},{"code":"두개의 boolean expression","type":"codeVoice"},{"type":"text","text":"을 "},{"type":"codeVoice","code":"파라미터로 받는 논리 연산자"},{"type":"text","text":"이다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"code":"condition을 이어 붙인다","type":"codeVoice"},{"text":"고 했는데, 이렇게 이어 붙여진 condition을 ","type":"text"},{"type":"strong","inlineContent":[{"text":"condition-list","type":"text"}]},{"text":"라고 부른다","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"condition","type":"text"}]},{"type":"text","text":"은 뭘까?"},{"text":" ","type":"text"},{"code":"expression","type":"codeVoice"},{"type":"text","text":", "},{"code":"availability-condition","type":"codeVoice"},{"text":", ","type":"text"},{"code":"case-condition","type":"codeVoice"},{"type":"text","text":", "},{"type":"codeVoice","code":"optional-binding-condition"}]}]},{"content":[{"inlineContent":[{"code":"while, if, guard","type":"codeVoice"},{"text":" 문에는 ","type":"text"},{"type":"codeVoice","code":"condition-list"},{"type":"text","text":"를 쓰지만, "},{"code":"repeat-while","type":"codeVoice"},{"type":"text","text":" 문에서는 "},{"code":"condition","type":"codeVoice"},{"type":"text","text":"만 쓸 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"콤마로 여러 condition을 이어붙이는 것이 ","type":"text"},{"inlineContent":[{"text":"허용된 곳","type":"text"}],"type":"strong"},{"text":"과 ","type":"text"},{"inlineContent":[{"text":"아닌 곳","type":"text"}],"type":"strong"},{"text":"이 있는 것이다.","type":"text"}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["if let result = result && result {","if let result = result , result {"]},{"type":"paragraph","inlineContent":[{"text":"이 두 구문이 어떻게 다른 것일까?","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"첫번째 구문은 하나의 "},{"code":"expression","type":"codeVoice"},{"type":"text","text":"이다. 양쪽의 A && B 를 논리연산자로 계산해 참, 거짓의 결과를 가져온다"},{"type":"text","text":" "},{"type":"text","text":"두번째 구문은 "},{"type":"codeVoice","code":"expression"},{"text":", ","type":"text"},{"code":"expression","type":"codeVoice"},{"text":"으로 이루어진 ","type":"text"},{"type":"codeVoice","code":"condition-list"},{"type":"text","text":"이다. 즉 두개의 "},{"code":"expression","type":"codeVoice"},{"type":"text","text":"이 각 각 참이 되어야 if문이 실행된다. 옵셔널 바인딩이 성공하고, 그 값이 참이어야 실행되는 코드를 만들 수 있다."}]}]}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/docs.swift.org\/swift-book\/ReferenceManual\/Statements.html#grammar_condition","isActive":true,"type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/docs.swift.org\/swift-book\/ReferenceManual\/Statements.html#grammar_condition-list","type":"reference","isActive":true}]}]},{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/soojin.ro\/blog\/swift-comma-vs-and-operator","isActive":true}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}],"kind":"article","schemaVersion":{"patch":0,"major":0,"minor":3},"metadata":{"roleHeading":"Article","role":"article","title":"211011 condition, condition-list, flow chart, 일반화추상화은닉화캡슐화, Ground rules, Daily Scrum, 메소드명","modules":[{"name":"Ari's Notes"}]},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"sections":[],"references":{"https://docs.swift.org/swift-book/ReferenceManual/Statements.html#grammar_condition-list":{"identifier":"https:\/\/docs.swift.org\/swift-book\/ReferenceManual\/Statements.html#grammar_condition-list","title":"condition-list","type":"link","titleInlineContent":[{"type":"text","text":"condition-list"}],"url":"https:\/\/docs.swift.org\/swift-book\/ReferenceManual\/Statements.html#grammar_condition-list"},"https://soojin.ro/blog/swift-comma-vs-and-operator":{"identifier":"https:\/\/soojin.ro\/blog\/swift-comma-vs-and-operator","title":"콤마(,)와 &&의 차이","type":"link","titleInlineContent":[{"type":"text","text":"콤마(,)와 &&의 차이"}],"url":"https:\/\/soojin.ro\/blog\/swift-comma-vs-and-operator"},"https://docs.swift.org/swift-book/ReferenceManual/Statements.html#grammar_condition":{"url":"https:\/\/docs.swift.org\/swift-book\/ReferenceManual\/Statements.html#grammar_condition","type":"link","title":"condition","titleInlineContent":[{"type":"text","text":"condition"}],"identifier":"https:\/\/docs.swift.org\/swift-book\/ReferenceManual\/Statements.html#grammar_condition"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211012_----,_properties,_keypath,_kvc,_ios,_hig.json b/docs/data/documentation/arinote/211012_----,_properties,_keypath,_kvc,_ios,_hig.json new file mode 100644 index 0000000..d22d828 --- /dev/null +++ b/docs/data/documentation/arinote/211012_----,_properties,_keypath,_kvc,_ios,_hig.json @@ -0,0 +1 @@ +{"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211012_----,_properties,_keypath,_kvc,_ios,_hig"]}],"metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","title":"211012 프로젝트, Properties, KeyPath, KVC, iOS, HIG"},"kind":"article","schemaVersion":{"minor":3,"patch":0,"major":0},"primaryContentSections":[{"content":[{"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","type":"heading","level":1},{"type":"paragraph","inlineContent":[{"type":"text","text":"10월 12일 (화)"}]},{"text":"학습 내용","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"inlineContent":[{"type":"text","text":"새벽에 프로젝트 STEP 1을 피드백 받기전에 좀 더 생각해볼 점은 없는지 완성한 코드를 보면서 고민하는 시간을 가졌다. 그리고 아침에 "},{"type":"codeVoice","code":"알라딘"},{"text":"과 만나서 고민했던 점을 나누고 코드를 개선한 후 PR을 올렸다. 이후에는 개인 공부시간을 가지기로 하고 프로퍼티와 KVC에 대해서 공부해보았다. 다 마치고 나서 목요일 활동학습인 iOS-HIG 부분을 예습해보았는데, 생각지도 못한 엄청난 양이다… 복습을 여러번 해야할 것 같다.","type":"text"}],"type":"paragraph"},{"anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","text":"문제점 \/ 고민한 점","type":"heading","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"가위(1), 바위(2), 보(3)를 좀더 명확하게 표시할 수 없을까?","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"연산 프로퍼티를 이용하여 randomNumber를 만들어보면 어떨까?","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"승패판정을 하는 메소드명을 명확하게 다시 지어주고 싶다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로퍼티의 종류와 내용을 까먹었다. 복습을 해야겠다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"공부하다가 ","type":"text"},{"type":"codeVoice","code":"KeyPath"},{"type":"text","text":" 라는 키워드를 얻었다. 이게 뭐지?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로젝트 코드를 보면서 개선할 수 있는 부분은 없는지 살펴보았다."}]}]}],"type":"unorderedList"},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"level":2,"text":"해결방법","type":"heading","anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"각 숫자를 의미를 담아 프로퍼티로 선언해주었더니 코드 내부의 가독성이 상승했다!","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"일단 연산 프로퍼티를 적용해보았는데, 좋아진건지 모르겠다. 로직은 그대로라… 리뷰어인 "},{"code":"Soll","type":"codeVoice"},{"text":"에게 물어보기로 하였다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Naming 파트를 다시한번 참고해보았는데.. 영어가 안된다… 영어공부를 해야할까…? 휴…","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Properties 파트와 KeyPath는 무엇인지 간단하게 정리해보았다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"역시 코드는 맨날 봐도 새롭게 다가온다. 좋은 방법이 떠올랐다!","type":"text"},{"text":" ","type":"text"},{"text":" ","type":"text"}]}]}]},{"anchor":"%EA%B3%B5%EB%B6%80%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC","type":"heading","level":2,"text":"공부내용 정리"},{"anchor":"Properties","type":"heading","text":"Properties","level":1},{"inlineContent":[{"type":"text","text":"프로퍼티 클래스, 구조체 또는 열거형 등에 관련된 값을 뜻한다."},{"type":"text","text":" "},{"text":"메서드 특정 타입에 관련된 함수를 뜻한다.","type":"text"}],"type":"paragraph"},{"text":"저장 프로퍼티 (Stored Properties)","anchor":"%EC%A0%80%EC%9E%A5-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-Stored-Properties","type":"heading","level":1},{"inlineContent":[{"type":"text","text":"클래스, 구조체의 인스턴스와 연관된 값을 저장하는 가장 단순한 개념의 프로퍼티"}],"type":"paragraph"},{"anchor":"%EC%A7%80%EC%97%B0-%EC%A0%80%EC%9E%A5-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-Lazy-Stored-Properties","type":"heading","text":"지연 저장 프로퍼티 (Lazy Stored Properties)","level":2},{"type":"paragraph","inlineContent":[{"text":"인스턴스를 생성할 때 프로퍼티에 값이 필요 없다면 프로퍼티를 옵셔널로 선언해줄 수 있다. 그러나 그것과는 조금 다른 용도로 필요할 때 값이 할당되는 지연 저장 프로퍼티가 있다. 지연 저장 프로퍼티는 호출이 있어야 값을 초기화하며, 이때 ","type":"text"},{"code":"lazy","type":"codeVoice"},{"text":" 키워드를 사용한다.","type":"text"},{"type":"text","text":" "},{"text":"상수는 인스턴스가 완전히 생성되기 전에 초기화해야 하므로 필요할 때 값을 할당하는 지연 저장 프로퍼티와는 맞지 않다. 따라서 지연 저장 프로퍼티는 ","type":"text"},{"type":"codeVoice","code":"var"},{"text":" 키워드를 사용하여 변수로 정의한다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"주로 ‘굳이 모든 저장 프로퍼티를 사용할 필요가 없다면?’ 혹은 ‘인스턴스를 초기화하면서 저장 프로퍼티로 쓰이는 인스턴스들이 한 번에 생성되어야 한다면?’ 이 질문의 답이 지연 저장 프로퍼티 사용이라고 볼 수 있다."},{"type":"text","text":" "},{"text":"지연 저장 프로퍼티를 잘 사용하면 불필요한 성능저하나 공간 낭비를 줄일 수 있다.","type":"text"}]},{"level":2,"text":"연산 프로퍼티 (Computed Properties)","anchor":"%EC%97%B0%EC%82%B0-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-Computed-Properties","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"저장 프로퍼티와 다르게 특정 상태에 따른 값을 연산하는 프로퍼티이다. 인스턴스 내.외부의 값을 연산하여 적절한 값을 돌려주는 접근자의 역할이나 은닉화된 내부의 프로퍼티 값을 간접적으로 설정하는 설정자의 역할을 할 수도 있다."},{"text":" ","type":"text"},{"type":"text","text":"연산 프로퍼티는 접근자인 "},{"type":"codeVoice","code":"get"},{"text":" 메서드만 구현해둔 것처럼 ","type":"text"},{"type":"codeVoice","code":"읽기 전용 상태"},{"text":"로 구현하기는 쉽지만, ","type":"text"},{"code":"쓰기 전용 상태로 구현할 수 없다는 단점","type":"codeVoice"},{"type":"text","text":"이 있다."}]},{"level":2,"type":"heading","anchor":"%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EA%B0%90%EC%8B%9C%EC%9E%90-Property-Observers","text":"프로퍼티 감시자 (Property Observers)"},{"inlineContent":[{"type":"text","text":"프로퍼티의 값이 변경됨에 따라 적절한 작업을 취할 수 있다. 프로퍼티 감시자는 "},{"type":"codeVoice","code":"프로퍼티의 값이 새로 할당될 때마다 호출"},{"text":"한다. 이때 변경되는 값이 현재의 값과 같더라도 호출한다. 지연 저장 프로퍼티에는 사용할 수 없다. 일반 저장 프로퍼티에만 사용할 수 있다. 또한 프로퍼티 재정의해 상속받은 저장 프로퍼티 또는 연산 프로퍼티에도 적용할 수 있다.","type":"text"},{"type":"text","text":" "},{"text":"프로퍼티 감시자에는 프로퍼티의 값이 변경되기 직전에 호출하는 ","type":"text"},{"type":"codeVoice","code":"willSet"},{"text":" 메서드와 프로퍼티의 값이 변경된 직후에는 호출하는 ","type":"text"},{"type":"codeVoice","code":"didSet"},{"text":" 메서드가 있다. willSet은 변경될 값이고, didSet은 ","type":"text"},{"code":"변경되기 전의 값","type":"codeVoice"},{"text":"이다. 매개변수의 이름을 따로 지정하지 않으면 ","type":"text"},{"type":"codeVoice","code":"willSet은 newValue"},{"type":"text","text":"가, "},{"type":"codeVoice","code":"didSet은 oldValue"},{"type":"text","text":"라는 매개변수 이름이 "},{"type":"strong","inlineContent":[{"text":"자동 지정","type":"text"}]},{"type":"text","text":"된다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"만약 프로퍼티 감시자가 있는 프로퍼티를 함수의 입출력 매개변수의 전달인자로 전달한다면 항상 ","type":"text"},{"type":"codeVoice","code":"willSet과 didSet 감시자"},{"text":"를 호출한다. 함수 내부에서 값이 변경되든 되지 않든 간에 함수가 종료되는 시점에 값을 다시 쓰기 때문이다.","type":"text"}]}]}],"type":"unorderedList"},{"level":1,"text":"Keypath","type":"heading","anchor":"Keypath"},{"type":"paragraph","inlineContent":[{"type":"text","text":"프로퍼티의 위치만 "},{"type":"codeVoice","code":"참조"},{"type":"text","text":"하도록 할 수 있는 방법이다. C로 치면 "},{"type":"codeVoice","code":"포인터"},{"text":"라고 보면 될 것 같다.","type":"text"},{"type":"text","text":" "},{"type":"codeVoice","code":"\\타입이름.경로.경로.경로"}]},{"type":"heading","anchor":"%ED%82%A4-%ED%8C%A8%EC%8A%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0","level":2,"text":"키 패스를 사용하는 이유"},{"inlineContent":[{"type":"text","text":"키패스는 "},{"type":"codeVoice","code":"Metaprogramming"},{"type":"text","text":"의 한 형태이다. 속성에 대한 위치를 "},{"type":"codeVoice","code":"참조"},{"type":"text","text":"하여 인스턴스의 속성을 동적으로 읽거나 쓴다."}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["struct Address {"," var town: String","}","","struct Person {"," var address: Address","}","","let address = Address(town: \"어딘가\")","var ari = Person(address: address)","let ariTown = ari[keyPath: \\Person.address.town] \/\/ 가져오기","print(ariTown) \/\/ 어딘가","","ari[keyPath: \\Person.address.town] = \"어디야\" \/\/ 수정하기","print(ari[keyPath: \\Person.address.town]) \/\/ 어디야"]},{"level":2,"type":"heading","text":"키패스의 종류","anchor":"%ED%82%A4%ED%8C%A8%EC%8A%A4%EC%9D%98-%EC%A2%85%EB%A5%98"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"AntyKeyPath","type":"codeVoice"},{"type":"text","text":" 타입이 지워진 KeyPath"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"PartialKeyPath","type":"codeVoice"},{"type":"text","text":" 부분적으로 타입이 지워진 KeyPath"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"KeyPath","type":"codeVoice"},{"text":" Read-only, 읽기 전용","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"WritableKeyPath"},{"text":" value type 인스턴스에 사용 가능. 변경 가능한 모든 프로퍼티에 대한 read & write access 제공 ","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"eferenceWritableKeyPath","type":"codeVoice"},{"type":"text","text":" 클래스의 인스턴스에 사용 가능. 변경 가능한 모든 프로퍼티에 대한 read & write access 제공. "}]}]}]},{"type":"heading","text":"KVC","anchor":"KVC","level":1},{"inlineContent":[{"type":"text","text":"Key-Value Coding의 약자이다. 객체의 값을 직접 가져오지않고, Key 또는 KeyPath를 이용해서 간접적으로 데이터를 가져오거나 수정하는 방법이다."}],"type":"paragraph"},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/Properties.html"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"identifier":"https:\/\/developer.apple.com\/library\/archive\/documentation\/Cocoa\/Conceptual\/KeyValueCoding\/index.html","type":"reference"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/library\/archive\/documentation\/General\/Conceptual\/DevPedia-CocoaCore\/KeyValueCoding.html","type":"reference","isActive":true}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/zeddios.tistory.com\/1218","isActive":true}]}]},{"content":[{"inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/learnappmaking.com\/swift-keypath-how-to\/"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"sections":[],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211012_----,_Properties,_KeyPath,_KVC,_iOS,_HIG"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"references":{"https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/index.html":{"type":"link","url":"https:\/\/developer.apple.com\/library\/archive\/documentation\/Cocoa\/Conceptual\/KeyValueCoding\/index.html","title":"KeyValueCoding","titleInlineContent":[{"type":"text","text":"KeyValueCoding"}],"identifier":"https:\/\/developer.apple.com\/library\/archive\/documentation\/Cocoa\/Conceptual\/KeyValueCoding\/index.html"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://docs.swift.org/swift-book/LanguageGuide/Properties.html":{"type":"link","url":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/Properties.html","title":"Properties","titleInlineContent":[{"text":"Properties","type":"text"}],"identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/Properties.html"},"https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/KeyValueCoding.html":{"type":"link","url":"https:\/\/developer.apple.com\/library\/archive\/documentation\/General\/Conceptual\/DevPedia-CocoaCore\/KeyValueCoding.html","title":"archive\/KVC","titleInlineContent":[{"text":"archive\/KVC","type":"text"}],"identifier":"https:\/\/developer.apple.com\/library\/archive\/documentation\/General\/Conceptual\/DevPedia-CocoaCore\/KeyValueCoding.html"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://zeddios.tistory.com/1218":{"type":"link","url":"https:\/\/zeddios.tistory.com\/1218","title":"Key-Value Coding(KVC) \/ KeyPath in Swift","titleInlineContent":[{"text":"Key-Value Coding(KVC) \/ KeyPath in Swift","type":"text"}],"identifier":"https:\/\/zeddios.tistory.com\/1218"},"https://learnappmaking.com/swift-keypath-how-to/":{"type":"link","url":"https:\/\/learnappmaking.com\/swift-keypath-how-to\/","title":"KeyPath","titleInlineContent":[{"text":"KeyPath","type":"text"}],"identifier":"https:\/\/learnappmaking.com\/swift-keypath-how-to\/"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211013_naming,_if-else,_switch,_----_----,_reset,_reflog.json b/docs/data/documentation/arinote/211013_naming,_if-else,_switch,_----_----,_reset,_reflog.json new file mode 100644 index 0000000..320310e --- /dev/null +++ b/docs/data/documentation/arinote/211013_naming,_if-else,_switch,_----_----,_reset,_reflog.json @@ -0,0 +1 @@ +{"metadata":{"modules":[{"name":"Ari's Notes"}],"role":"article","title":"211013 Naming, if-else, switch, 반복문과 재귀함수, reset, reflog","roleHeading":"Article"},"variants":[{"paths":["\/documentation\/arinote\/211013_naming,_if-else,_switch,_----_----,_reset,_reflog"],"traits":[{"interfaceLanguage":"swift"}]}],"kind":"article","sections":[],"schemaVersion":{"minor":3,"major":0,"patch":0},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211013_Naming,_if-else,_switch,_----_----,_reset,_reflog","interfaceLanguage":"swift"},"primaryContentSections":[{"content":[{"type":"heading","text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned","level":1},{"type":"paragraph","inlineContent":[{"type":"text","text":"10월 13일 (수)"}]},{"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"type":"heading"},{"type":"paragraph","inlineContent":[{"text":"오늘은 ","type":"text"},{"type":"codeVoice","code":"Soll"},{"type":"text","text":"의 피드백을 참고하여 "},{"code":"알라딘","type":"codeVoice"},{"type":"text","text":"과 함께 STEP 1의 코드를 개선해보기로 하였다. 역시 피드백이 참 중요하고 또 도움이 많이 된다는 느낌을 받았다. 코드가 전이랑 확연하게 좋아지는 것은 물론 변동사항이 꽤 많았다. 또 개선되면서 묵찌빠에서도 활용할 수 있는 좋은 여러가지 아이디어가 떠오르기도 하면서 재밌는 시간이였다. 코드개선을 마치고 나서는 정리하고 있던 프로젝트 진행과정, README를 다듬어주었고, 오늘 날씨가 좋길래 새로운 카페에 도착하여 어제 예습하다 말았던 HIG를 다시 정독하기 시작했다."}]},{"type":"heading","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","text":"문제점 \/ 고민한 점","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"잊지 말자. ","type":"text"},{"type":"codeVoice","code":"naming"},{"text":"을 할 때 ","type":"text"},{"code":"함수나 변수의 역할","type":"codeVoice"},{"type":"text","text":"에 집중하며, 최대한 "},{"inlineContent":[{"text":"하나의 역할을 맡는 이름","type":"text"}],"type":"strong"},{"type":"text","text":"을 지어주면 될 것."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"가위바위보를 별도의 사용자 정의 타입으로 구현해주었을 때 사용자의 입력을 검사하는 좋은 아이디어가 없을까?"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"파라미터의 순서의 기준을 정하지 않았다는 사실을 깨달았다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"if와 switch의 차이와 장단점이 뭘까?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"반복문과 재귀함수의 차이와 장단점은 뭐지?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"enum 내부에 있는 프로퍼티를 외부에서 바로 사용하려면 어떻게 해야하지?"}]}]},{"content":[{"inlineContent":[{"text":"커밋을 잘못 작성하여 reset후 강제 push를 진행하였다. 그런데 왜 강제 push를 지향하는지, 또 reset 후 되돌리려면 reflog를 사용했었는데, 알라딘에게 설명해주려니 reflog가 무슨 역할을 했었는지 기억이 안나서 말이 안나왔다…!!","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","level":2,"type":"heading","text":"해결방법"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"if-else","type":"codeVoice"},{"text":"와 ","type":"text"},{"code":"switch","type":"codeVoice"},{"text":"의 차이","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"if-else"},{"type":"text","text":" boolean의 결과 값을 내놓는 조건문에 따라 true, false에 해당하는 각각 두 개의 흐름으로 갈라진다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"switch","type":"codeVoice"},{"text":" 변수를 입력 받아 미리 정해놓은 여러 값들과의 일치여부를 판단하여 switch문 내의 control flow를 결정한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"if-else"},{"text":"는 조건을 숫자뿐아니라 비교 연산등 마음대로 사용 가능하기 때문에 언뜻 봤을 땐 더 편해보인다. 하지만 if-else는 위에서부터 조건을 하나 하나 ","type":"text"},{"type":"codeVoice","code":"순차적"},{"type":"text","text":"으로본다. switch문은 들어오는 조건을 보고 "},{"inlineContent":[{"type":"text","text":"즉각"}],"type":"strong"},{"text":" ","type":"text"},{"type":"codeVoice","code":"조건에 해당하는 문장으로 진입"},{"type":"text","text":"한다. 그러나 switch는 break, return을 사용하지 않으면 뒤에 조건까지 줄줄이 읽게 되니 꼭 진입시 탈출조건을 걸어주도록 하자."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"반복문"},{"text":"과 ","type":"text"},{"type":"codeVoice","code":"재귀함수"},{"text":"의 차이","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"반복문은 속도가 상대적으로 빠르다는 장점이 있지만, 코드가 복잡해지는 단점이 있다. 재귀함수는 상대적으로 간결한 코드로 작성할 수 있다는 장점이 있지만 반복문에 비해 속도가 느리다는 단점이 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"재귀함수는 함수를 반복적으로 호출하기 때문에 스택 메모리를 사용한다. (스택 오버플로우가 발생할 수 있다.) 반면 반복문은 메모리 힙을 사용한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"성능 측면에서만 본다면 재귀함수는 사용하지 않는 것이 맞다. 반복문으로 구현했을 때 보다 메모리나 속도 등 성능적인 측면에서 많이 뒤쳐지기 때문이다. 하지만 예전처럼 Hardware가 좋지 못해 Software의 속도를 극한까지 끌어올려야 하는 시대가 아니기 때문에 가독성도 고려하여 프로그래밍을 해야한다. 특히 여러사람이 개발에 참여한다면 가독성은 더욱 중요하다. 결국 프로그램의 목적에 맞게 판단해서 사용해야한다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"enum 내부에 있는 property를 사용하려고 하니까 에러가 나와서 찾아보니 타입 내부에 있는 property를 인스턴스 생성 없이 바로 사용하려면 "},{"code":"static","type":"codeVoice"},{"type":"text","text":" 키워드로 선언해주어야 한다는 사실을 알아냈다. 개념이 헷갈려서 팀원인 알라딘에게도, 의존 모둠인 제이티와 chacha에게도 물어봐서 static 개념에 대해서 이해하려고 노력했다. 메서드도 마찬가지로 기본적으로 "},{"type":"codeVoice","code":"인스턴스 메서드"},{"text":"이고 인스턴스 생성 없이 바로 사용하려면 ","type":"text"},{"type":"codeVoice","code":"static"},{"type":"text","text":" 키워드를 붙여주면 된다."}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"reset"},{"text":" 내가 commit한 것이 마음에 들지 않을 때 reset을 이용하여 초기화 해줄 수 있다. ","type":"text"},{"type":"codeVoice","code":"--hard"},{"text":" 옵션을 사용하면 작업중인 내용을 가져오지 않고 완전히 삭제가 된다. ","type":"text"},{"inlineContent":[{"type":"text","text":"아직 commit을 하지 않았다면 git reset hard를 사용하는 것은 주의"}],"type":"strong"},{"text":"해야 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"reflog","type":"codeVoice"},{"type":"text","text":" 해당 명령어를 사용하면 "},{"type":"strong","inlineContent":[{"type":"text","text":"reset으로 지웠던 모든 이력"}]},{"type":"text","text":"을 볼 수 있다."},{"text":" ","type":"text"},{"text":" ","type":"text"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"identifier":"https:\/\/aahc.tistory.com\/6","isActive":true,"type":"reference"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"isActive":true,"type":"reference","identifier":"http:\/\/melonicedlatte.com\/2021\/05\/10\/001900.html"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/babbab2.tistory.com\/122","type":"reference","isActive":true}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/www.hackingwithswift.com\/read\/0\/18\/static-properties-and-methods","isActive":true,"type":"reference"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}],"kind":"content"}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"references":{"https://aahc.tistory.com/6":{"type":"link","identifier":"https:\/\/aahc.tistory.com\/6","url":"https:\/\/aahc.tistory.com\/6","title":"if문과 switch","titleInlineContent":[{"text":"if문과 switch","type":"text"}]},"https://babbab2.tistory.com/122":{"type":"link","identifier":"https:\/\/babbab2.tistory.com\/122","url":"https:\/\/babbab2.tistory.com\/122","title":"static property [1]","titleInlineContent":[{"text":"static property [1]","type":"text"}]},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"http://melonicedlatte.com/2021/05/10/001900.html":{"type":"link","identifier":"http:\/\/melonicedlatte.com\/2021\/05\/10\/001900.html","url":"http:\/\/melonicedlatte.com\/2021\/05\/10\/001900.html","title":"반복문과 재귀함수","titleInlineContent":[{"text":"반복문과 재귀함수","type":"text"}]},"https://www.hackingwithswift.com/read/0/18/static-properties-and-methods":{"type":"link","identifier":"https:\/\/www.hackingwithswift.com\/read\/0\/18\/static-properties-and-methods","url":"https:\/\/www.hackingwithswift.com\/read\/0\/18\/static-properties-and-methods","title":"static property [2]","titleInlineContent":[{"text":"static property [2]","type":"text"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211014_hig,_----,_---,_thread,_serial_queue,_xcode_debugging.json b/docs/data/documentation/arinote/211014_hig,_----,_---,_thread,_serial_queue,_xcode_debugging.json new file mode 100644 index 0000000..d169305 --- /dev/null +++ b/docs/data/documentation/arinote/211014_hig,_----,_---,_thread,_serial_queue,_xcode_debugging.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","type":"heading","anchor":"TIL-Today-I-Learned","level":1},{"inlineContent":[{"type":"text","text":"10월 14일 (목)"}],"type":"paragraph"},{"type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용"},{"inlineContent":[{"type":"text","text":"오늘은 iOS-HIG에 대해서 활동학습을 가졌다. 아이폰의 기본 앱들을 참고하여 어떤 HIG가 적용되었는지 분석하는 시간을 가졌는데, 예습으로 쭉 정독하는 방법보다 훨씬 더 머릿속에 잘 들어오는 마법같은 시간이였다. 우리 팀은 아이폰의 "},{"type":"codeVoice","code":"설정"},{"text":" 앱을 분석해보았고, 야곰과 함께 ","type":"text"},{"code":"modal","type":"codeVoice"},{"text":"과 ","type":"text"},{"type":"codeVoice","code":"navigation"},{"text":"의 차이점을 분석하고 알아보는 유익한 시간이였다. 이후에는 알라딘과 프로젝트 STEP 2를 진행하는 것으로 하루를 보냈다.","type":"text"}],"type":"paragraph"},{"type":"heading","level":2,"anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","text":"문제점 \/ 고민한 점"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"활동학습 때 나왔던 의문점","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"modality과 navigation의 차이는 무엇일까?"}]}]},{"content":[{"inlineContent":[{"text":"text field와 text view의 차이점은 무엇일까?","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Pull-Down Buttons와 Pop-Up Buttons에 대해서 알아보자."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뒤로가기 버튼을 꾹 누르면 쌓여있는 화면리스트가 나오는데 이건 무슨 기능이지?"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"프로젝트 진행중에 나왔던 의문점","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"재귀함수를 쓰는것이 과연 적절할까?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"반복문으로 어지러워진 코드 내부를 어떻게하면 깔끔하게 정리할 수 있을까?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"누구의 턴인지 표현하기 위해 Bool 타입을 활용했는데, 사용자 정의 타입으로 구현해도 괜찮을 것 같지만 과연 적절한 방법일까?","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"재귀함수를 써도 return이 되면 스택에서 안쌓이는게 아닌가?"}]}]}]}]}]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"text":"해결방법","level":2,"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"modality는 임시화면, 사용자의 이전 컨텍스트와 별개의 화면이며, 종료하려면 명시적인 작업이 필요한 임시 모드로 컨텐츠를 표시하는 기술이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"navigation은 앱의 지도라고 이해했다. modal처럼 샛길로 빠지는 것이 아닌 정해져있는 길?로 이해했다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Pop-Up Buttons은 상호간에 추가옵션을 공통적으로 제공하는 버튼이다. (계속 바뀌는 메뉴)"}]}]},{"content":[{"inlineContent":[{"text":"Pull-Down Buttons은 버튼과 연관된 액션들을 보여준다. 카메라 버튼이면 카메라와 연관된 액션을 보여준다. (항상 같은 메뉴)","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"뒤로가기 버튼을 꾹 누르면 쌓여있는 화면리스트가 내가 생각하기엔 Pull-Down Button인 것 같다. (가이드에 비슷한게 그것 뿐..)","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"프로젝트를 진행하면서 들여쓰기를 하지 않으려고 재귀함수를 사용하여 게임을 구현해보았는데, 아무리 생각해도 성능적으로 좋지 못하다는 생각이 들어서 반복문으로 재구현을 했다. 덕분에 재귀함수와 반복문의 차이점을 이해하는데 많은 도움이 되었다."},{"text":" ","type":"text"},{"type":"image","identifier":"https:\/\/i.imgur.com\/vEzvqt0.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"재귀함수를 호출하고 return을 해도 바로 종료되는 것이 아니라 재귀함수가 계속 스택에 쌓이다가 마지막에 호출된 함수가 종료되면, 그제서야 쌓였던 재귀함수가 return 되면서 차례대로 제거된다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Coda가 잠깐 와서 준 꿀팁"},{"type":"text","text":": 재귀함수를 디버깅모드로 확인하여 스택이 쌓이는지 확인할 수 있다. 디버깅 모드시 콘솔에 "},{"code":"n","type":"codeVoice"},{"text":"을 입력하고 엔터를 하면 다음 코드로 이어진다.","type":"text"}]}]}],"type":"unorderedList"}]}]},{"text":"해결하지 못한 점","type":"heading","level":2,"anchor":"%ED%95%B4%EA%B2%B0%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%A0%90"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"repeat-while문"},{"text":"에 내부는 최대한 고쳐보았으나 좋은 방법이 떠오르지가 않아서 리뷰어인 ","type":"text"},{"type":"codeVoice","code":"Soll"},{"text":"에게 조언을 구하기로 하였다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"누구의 턴인지 표현하기 위해 "},{"type":"codeVoice","code":"Bool"},{"type":"text","text":"을 활용하는 법도 충분히 가독성면에서 괜찮다는 판단을 내렸지만, 사용자 정의 타입으로 확장성을 향상시킬 수 있는지 궁금하여 이것도 "},{"type":"codeVoice","code":"Soll"},{"type":"text","text":"에게 PR을 보내면서 조언을 구해보기로 하였다!"}]}]}],"type":"unorderedList"},{"anchor":"%EA%B3%B5%EB%B6%80%ED%95%B4%EB%B3%BC-%ED%82%A4%EC%9B%8C%EB%93%9C","text":"공부해볼 키워드","level":2,"type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"Thread"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Serial Queue"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"Xcode debugging","type":"codeVoice"},{"text":" ","type":"text"},{"type":"text","text":" "}]}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/","isActive":true,"type":"reference"}]}]}]}]}]}]}],"metadata":{"roleHeading":"Article","role":"article","title":"211014 HIG, 재귀함수, 반복문, Thread, Serial Queue, Xcode debugging","modules":[{"name":"Ari's Notes"}]},"sections":[],"variants":[{"paths":["\/documentation\/arinote\/211014_hig,_----,_---,_thread,_serial_queue,_xcode_debugging"],"traits":[{"interfaceLanguage":"swift"}]}],"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211014_HIG,_----,_---,_Thread,_Serial_Queue,_Xcode_debugging","interfaceLanguage":"swift"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"schemaVersion":{"minor":3,"major":0,"patch":0},"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/vEzvqt0.png":{"identifier":"https:\/\/i.imgur.com\/vEzvqt0.png","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/vEzvqt0.png"}]},"https://developer.apple.com/design/human-interface-guidelines/":{"type":"link","title":"iOS-HIG","identifier":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/","titleInlineContent":[{"type":"text","text":"iOS-HIG"}],"url":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211015_lldb,_gcd,_thread,_serial_queue,_async.json b/docs/data/documentation/arinote/211015_lldb,_gcd,_thread,_serial_queue,_async.json new file mode 100644 index 0000000..03d4fea --- /dev/null +++ b/docs/data/documentation/arinote/211015_lldb,_gcd,_thread,_serial_queue,_async.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"content":[{"anchor":"TIL-Today-I-Learned","type":"heading","level":1,"text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"text":"10월 15일 (금)","type":"text"}]},{"type":"heading","text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"type":"paragraph","inlineContent":[{"type":"text","text":"오늘은 어제 디버깅을 하면서 알게된 키워드에 대해서 공부해보기로 하였다. 키워드를 얻어서 검색만 해보았는데 생각보다 많은 양의 정보가 쏟아졌다. 또한 앞으로 개발하는데 있어서 중요한 지식이라고 기재되어있는 곳들이 많았다. (보통 면접 준비로…) 아직은 내가 이해하기엔 어려운 내용이라고 느껴지기도 했고, 질문방에가서 의논해보니 나중에 배울 내용이라고 생각되어 오늘은 그냥 어떤 개념인지만 숙지하고 넘어가기로 하였다. 이후 서포터즈인 "},{"code":"Steven","type":"codeVoice"},{"text":"과 크루 ","type":"text"},{"code":"Summer","type":"codeVoice"},{"type":"text","text":"와 함께 이야기를 나누면서 뭘배우면 좋을까 의논하다가 지금은 디버깅을 어떻게 하는지만 배워도 충분하다고 조언을 들어서 강의를 추천받아 보기 시작했다!"},{"type":"text","text":" "},{"type":"text","text":" "}]},{"anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","text":"문제점 \/ 고민한 점","level":2,"type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Thread… serial queue… async sync..이게 다 뭐야!!!!!!","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"GCD는 또 뭐야…? 모르는 용어는 다 찾아보자."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"모르는걸 찾다보니 공부할 내용이 너무 많네.. 이걸 다 지금 공부하는게 맞는걸까?","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"디버깅을 잘하는 간지나는 개발자가 되고싶다.","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"level":2,"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","text":"해결방법","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"공부하다가 이걸 다 이해하기에 지금은 역부족이라고 느꼈고, 서포터즈인 ","type":"text"},{"type":"codeVoice","code":"Steven"},{"type":"text","text":"과 크루 "},{"type":"codeVoice","code":"Summer"},{"type":"text","text":"가 계셔서 바로 들어가서 물어보았더니 해결이 되었다."}]},{"items":[{"content":[{"inlineContent":[{"inlineContent":[{"text":"받았던 답변","type":"text"}],"type":"strong"},{"text":" ","type":"text"},{"text":"GCD, Dispath Queue 등등 아직은 배울 내용이 아니다. 디버깅 하는 법만 간단히 배워도 충분하다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"디버깅 잘하는 간지나는 개발자가 되고 싶다고 했더니, 간지나는 개발자가 되려면 마우스 없이 키보드로만 개발할 수 있는 방법이 있다면서 나중에 "},{"type":"codeVoice","code":"vim"},{"text":"에 대해서 알아보라고 조언을 주셨다. 꿀팁 굿…ㅋ 나중에 배워서 꼭 써먹어봐야지.","type":"text"}]}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"text":"공부내용 정리","anchor":"%EA%B3%B5%EB%B6%80%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC","type":"heading","level":2},{"type":"paragraph","inlineContent":[{"type":"text","text":"LLDB가 무엇인지 알기 위해서는 먼저 LLVM에 대한 간단한 이해를 하는 것이 좋다."}]},{"type":"heading","level":3,"text":"LLVM","anchor":"LLVM"},{"type":"paragraph","inlineContent":[{"text":"Apple에서 진행한 Compiler에 필요한 Toolchain 개발 프로젝트","type":"text"}]},{"text":"특징","type":"heading","level":3,"anchor":"%ED%8A%B9%EC%A7%95"},{"inlineContent":[{"type":"text","text":"컴포넌트들의 재사용성을 중시해서 모듈화가 잘 되어있다."}],"type":"paragraph"},{"type":"heading","anchor":"%EC%84%9C%EB%B8%8C-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8","level":3,"text":"서브 프로젝트"},{"inlineContent":[{"text":"모듈화 되어있는 컴포넌트들을 이용해 진행된 프로젝트로 LLVM Core, Clang, libc++, LLDB 등이 있다.","type":"text"}],"type":"paragraph"},{"level":2,"anchor":"LLDB","type":"heading","text":"LLDB"},{"inlineContent":[{"text":"LLVM의 Debugger Component를 개발하는 서브 프로젝트다. LLVM 프로젝트를 통해 개발된 Clang Expression Parser, LLVM Diassembler 등 Low-Level 컨트롤이 가능한 모듈들로 이루어져 있어, 기계어에 가까운 영역까지 디버깅 가능하다는 장점이 있다. C, C++, Objective-C, Swift를 지원하며 현재 Xcode의 기본 디버거로 내장되어 있다. LLDB와 함께라면 실제 프로그램이 어떤 식으로 동작하는지 더 깊이 이해할 수 있다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Xcode IDE에서 LLDB 콘솔은 실행중인 프로젝트의 프로세스가 Breakpoint에서 멈추거나, pause 버튼을 통해 실행이 일시정지 되면 Xcode 화면 하단 Debug 콘솔에 나타난다."},{"text":" ","type":"text"},{"text":"","type":"text"}]},{"level":3,"type":"heading","text":"LLDB 명령어 기초 문법","anchor":"LLDB-%EB%AA%85%EB%A0%B9%EC%96%B4-%EA%B8%B0%EC%B4%88-%EB%AC%B8%EB%B2%95"},{"type":"paragraph","inlineContent":[{"code":"(lldb) command [subcommand] -option \"this is argument\"","type":"codeVoice"}]},{"type":"paragraph","inlineContent":[{"text":"Command, Subcommand, Option, Argument들로 이루어져 있고, 띄어쓰기로 구분한다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Command와 Subcommand는 LLDB 내 Object의 이름이다. (Etc, breakpoint, watchpoint, set, list … ) 이들은 모두 계층화 되어있어 Command에 따라 사용가능한 Subcommand 종류가 다르다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Option의 경우 Command 뒤 어느 곳에든 위치 가능하며, "},{"type":"codeVoice","code":"-"},{"type":"text","text":"(hyphen)으로 시작한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Argument에 공백이 포함되는 경우도 있기 때문에 “”로 묶어줄 수 있다."}]}]}]},{"inlineContent":[{"type":"codeVoice","code":"(lldb) breakpoint set --file test.c --line 12"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"breakpoint (Command)와 set (Subcommand)을 이용하며"},{"type":"text","text":" "},{"text":"–file option을 통해 test.c 파일 내","type":"text"},{"type":"text","text":" "},{"type":"text","text":"–line option을 통해 12번째 라인에"},{"text":" ","type":"text"},{"text":"중단점을 set 해준다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"LLDB에는 수많은 명령어와 해당하는 Subcommand, Option들이 존재한다. 기억이 가물가물 하거나 필요한 기능이 있는지 확인할 때는 도움 받을만한 Command를 유용하게 사용할 수 있다."}]},{"text":"Help","type":"heading","anchor":"Help","level":3},{"type":"paragraph","inlineContent":[{"text":"해당 문법으로 사용가능한 Subcommand, Option 리스트나 사용법을 보여주는 명령어","type":"text"}]},{"type":"heading","anchor":"LLDB%EC%97%90%EC%84%9C-%EC%A0%9C%EA%B3%B5%ED%95%98%EB%8A%94-Command%EA%B0%80-%EA%B6%81%EA%B8%88%ED%95%98%EB%8B%A4%EB%A9%B4","level":3,"text":"LLDB에서 제공하는 Command가 궁금하다면,"},{"type":"paragraph","inlineContent":[{"code":"(lldb) help","type":"codeVoice"}]},{"level":3,"anchor":"%ED%8A%B9%EC%A0%95-Command%EC%9D%98-Subcommand%EB%82%98-Option%EC%9D%B4-%EA%B6%81%EA%B8%88%ED%95%98%EB%8B%A4%EB%A9%B4","text":"특정 Command의 Subcommand나, Option이 궁금하다면,","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"(lldb) help breakpoint"},{"type":"text","text":" "},{"code":"(lldb) help breakpoint set","type":"codeVoice"}]},{"type":"heading","text":"Apropos","level":3,"anchor":"Apropos"},{"type":"paragraph","inlineContent":[{"text":"원하는 기능의 명령어가 있는지 관련 키워드를 통해 알아볼 수 있는 명령어","type":"text"}]},{"text":"referent count를 체크할 수 있는 명령어가 있을까? 궁금하다면,","type":"heading","anchor":"referent-count%EB%A5%BC-%EC%B2%B4%ED%81%AC%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%AA%85%EB%A0%B9%EC%96%B4%EA%B0%80-%EC%9E%88%EC%9D%84%EA%B9%8C-%EA%B6%81%EA%B8%88%ED%95%98%EB%8B%A4%EB%A9%B4","level":3},{"inlineContent":[{"type":"codeVoice","code":"(lldb) apropos \"refernce count\""}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"결과"},{"text":" ","type":"text"},{"type":"text","text":"The following commands may relate to ‘reference count’:"},{"type":"text","text":" "},{"text":"refcount – Inspect the reference count data for a Swift object","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"anchor":"Breakpoint-%EB%8B%A4%EB%A3%A8%EB%8A%94-%EB%B0%A9%EB%B2%95","level":1,"type":"heading","text":"Breakpoint 다루는 방법"},{"text":"Breakpoint를 만드는 기본적인 명령어 구조","anchor":"Breakpoint%EB%A5%BC-%EB%A7%8C%EB%93%9C%EB%8A%94-%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9D%B8-%EB%AA%85%EB%A0%B9%EC%96%B4-%EA%B5%AC%EC%A1%B0","type":"heading","level":3},{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"(lldb) breakpoint set [option] “arguments”"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"줄여서는,"},{"text":" ","type":"text"},{"code":"(lldb) br s [option] “arguments”","type":"codeVoice"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"대부분의 명령어와 옵션들은 command 맨 앞 1~2개 알파벳으로 줄여서 사용할 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"level":3,"text":"Function Name","anchor":"Function-Name","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"특정 이름을 가진 모든 함수에 -name (-n) option을 이용해 break를 걸 수 있다."},{"text":" ","type":"text"},{"type":"codeVoice","code":"(lldb) breakpoint —name viewDidLoad"},{"type":"text","text":" "},{"type":"codeVoice","code":"(lldb) b -n viewDidLoad"},{"type":"text","text":" \/\/ 줄여서 사용."}]},{"inlineContent":[{"text":"또한 -func-regex (-r) option을 이용해 정규표현식을 활용할 수도 있다.","type":"text"},{"type":"text","text":" "},{"type":"codeVoice","code":"(lldb) breakpoint set —func-regex ‘^hello’"},{"type":"text","text":" "},{"type":"codeVoice","code":"(lldb) br s -r ‘^hello’"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"breakpoint set —func-regex","type":"codeVoice"},{"text":" 는 줄여서 ","type":"text"},{"code":"rb","type":"codeVoice"},{"type":"text","text":"로도 사용 가능"},{"text":" ","type":"text"},{"type":"codeVoice","code":"(lldb) rb ‘^hello’"}]}]}]},{"anchor":"File","level":3,"type":"heading","text":"File"},{"type":"paragraph","inlineContent":[{"type":"text","text":"파일의 이름과 line 번호를 이용해 break를 걸기 위해서는 -file ("},{"type":"codeVoice","code":"-f"},{"type":"text","text":"), -line ("},{"code":"-n","type":"codeVoice"},{"type":"text","text":") option을 이용할 수 있다."}]},{"text":"특정 파일의 20번째 line에서 break","anchor":"%ED%8A%B9%EC%A0%95-%ED%8C%8C%EC%9D%BC%EC%9D%98-20%EB%B2%88%EC%A7%B8-line%EC%97%90%EC%84%9C-break","level":3,"type":"heading"},{"inlineContent":[{"type":"codeVoice","code":"(lldb) br s. -file viewController.swift —line 20"},{"type":"text","text":" "},{"code":"(lldb) br s -f ViewController.swift -l 20","type":"codeVoice"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Breakpoint에 멈춰있는 프로세스의 실행 지점을 변경할 수 있다.
멈춰있는 breakpoint line의 우측에 "},{"type":"codeVoice","code":"녹색 햄버거 버튼"},{"type":"text","text":"을 위 아래로 잡고 드래그하면 다음 실행 지점을 변경할 수 있다."}]}]}],"type":"unorderedList"},{"text":"Condition","type":"heading","anchor":"Condition","level":3},{"type":"paragraph","inlineContent":[{"type":"text","text":"-condition ("},{"code":"-c","type":"codeVoice"},{"type":"text","text":") option을 이용하면 breakpoint에서 원하는 조건을 걸 수도 있다. -c option 뒤의 expression이 "},{"type":"codeVoice","code":"true인 경우"},{"type":"text","text":"에만 breakpoint에서 멈춥니다."}]},{"level":3,"text":"viewWillAppear 호출시, animated가 true인 경우에만","type":"heading","anchor":"viewWillAppear-%ED%98%B8%EC%B6%9C%EC%8B%9C-animated%EA%B0%80-true%EC%9D%B8-%EA%B2%BD%EC%9A%B0%EC%97%90%EB%A7%8C"},{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"lldb) breakpoint set —name “viewWillAppear” —condition animated"},{"text":" ","type":"text"},{"type":"codeVoice","code":"(lldb) br s -n “viewWillAppear” -c animated"}]},{"level":3,"text":"Command 실행 & AutoContinue","type":"heading","anchor":"Command-%EC%8B%A4%ED%96%89--AutoContinue"},{"type":"paragraph","inlineContent":[{"text":"-command (-C) option을 이용하면 ","type":"text"},{"code":"break시 원하는 lldb command를 실행","type":"codeVoice"},{"type":"text","text":"할 수 있다."}]},{"inlineContent":[{"type":"codeVoice","code":"(lldb) breakpoint set -n “viewDidLoad” —command “po &arg1 -G1”"},{"type":"text","text":" "},{"code":"(lldb) br s -n “viewDidLoad” -C “po $arg1” -G1","type":"codeVoice"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"-auto-continue (-G) option의 기능은 auto continue로, command 실행 후 "},{"type":"codeVoice","code":"break에 걸린 채로 있지 않고 프로그램을 자동 진행"},{"type":"text","text":"하게 해준다."}],"type":"paragraph"},{"inlineContent":[{"type":"codeVoice","code":"(lldb) regex-brea"},{"text":"는 간단하게 Breakpoint 생성을 할 수 있도록 도와주는 Shorthand Command이다. ","type":"text"},{"code":"(lldb) b","type":"codeVoice"},{"type":"text","text":"로 줄여서 사용할 수 있다."}],"type":"paragraph"},{"level":3,"text":"사용방법","anchor":"%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95","type":"heading"},{"items":[{"content":[{"inlineContent":[{"text":"특정 이름을 가진 function에서 break","type":"text"},{"text":" ","type":"text"},{"code":"(lldb) b viewDidLoad","type":"codeVoice"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"현재 파일 20번째 line에서 break","type":"text"},{"type":"text","text":" "},{"type":"codeVoice","code":"(lldb) b 20"}]}]},{"content":[{"inlineContent":[{"text":"특정 파일 20번째 line에서 break","type":"text"},{"type":"text","text":" "},{"type":"codeVoice","code":"(lldb) b ViewController.swift:12"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"현재 파일 내 특정 text를 포함한 line에서 break","type":"text"},{"type":"text","text":" "},{"code":"(lldb) b \/stop here\/","type":"codeVoice"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"특정 주소값에서 break"},{"text":" ","type":"text"},{"type":"codeVoice","code":"(lldb) b 0x1234000

"}]}]}],"type":"unorderedList"},{"type":"heading","level":3,"text":"Breakpoint 리스트 확인하기","anchor":"Breakpoint-%EB%A6%AC%EC%8A%A4%ED%8A%B8-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0"},{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"(lldb) breakpoint list command"},{"text":"를 통해 현재 프로그램에 생성되어있는 ","type":"text"},{"type":"codeVoice","code":"Breakpoint의 목록"},{"type":"text","text":"을 확인할 수 있다. 또한 이 목록 정보에는 Breakpoint의 id와 이름 hit-count 정보, enable 여부, source 상의 위치, 주소값 등등 유용한 정보가 포함되어 있다."}]},{"text":"hit-count란?","anchor":"hit-count%EB%9E%80","type":"heading","level":3},{"type":"paragraph","inlineContent":[{"type":"text","text":"프로그램 실행 중 활성 상태인 Breakpoint 지점이 실행되면, Debugger는 hit count를 1씩 늘려가며 기록한다. 하지만 Breakpoint가 걸려있다 하더라도 "},{"type":"strong","inlineContent":[{"type":"text","text":"disable 상태"}]},{"text":"이면 count되지 않는다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"text":"Breakpoint id를 통해 원하는 내용만 출력하거나, -brief (","type":"text"},{"type":"codeVoice","code":"-b"},{"type":"text","text":") option을 통해 간단한 내용을 확인해 볼 수도 있다."}]},{"text":"breakpoint 목록 전체 출력","level":3,"type":"heading","anchor":"breakpoint-%EB%AA%A9%EB%A1%9D-%EC%A0%84%EC%B2%B4-%EC%B6%9C%EB%A0%A5"},{"inlineContent":[{"code":"(lldb) breakpoint list","type":"codeVoice"},{"text":" ","type":"text"},{"type":"codeVoice","code":"(lldb) br list"}],"type":"paragraph"},{"anchor":"breakpoint-%EB%AA%A9%EB%A1%9D-%EA%B0%84%EB%8B%A8%ED%95%98%EA%B2%8C-%EC%B6%9C%EB%A0%A5","text":"breakpoint 목록 간단하게 출력","level":3,"type":"heading"},{"inlineContent":[{"type":"codeVoice","code":"(lldb) br list -b"}],"type":"paragraph"},{"type":"heading","text":"특정 id를 가진 breapoint의 정보만 출력","level":3,"anchor":"%ED%8A%B9%EC%A0%95-id%EB%A5%BC-%EA%B0%80%EC%A7%84-breapoint%EC%9D%98-%EC%A0%95%EB%B3%B4%EB%A7%8C-%EC%B6%9C%EB%A0%A5"},{"type":"paragraph","inlineContent":[{"code":"(lldb) br list 1","type":"codeVoice"}]},{"type":"heading","text":"Breakpoint 삭제 또는 비활성화 시키기","anchor":"Breakpoint-%EC%82%AD%EC%A0%9C-%EB%98%90%EB%8A%94-%EB%B9%84%ED%99%9C%EC%84%B1%ED%99%94-%EC%8B%9C%ED%82%A4%EA%B8%B0","level":2},{"inlineContent":[{"type":"text","text":"Delete, disable, Subcommand를 이용해 Breakpoint를 삭제하거나, 비활성화 할 수 있다."}],"type":"paragraph"},{"anchor":"breakpoint-%EC%A0%84%EC%B2%B4-%EC%82%AD%EC%A0%9C","text":"breakpoint 전체 삭제","type":"heading","level":3},{"inlineContent":[{"type":"codeVoice","code":"(lldb) breakpoint delete"},{"type":"text","text":" "},{"code":"(lldb) br de","type":"codeVoice"}],"type":"paragraph"},{"text":"특정 breakpoint 삭제","type":"heading","level":3,"anchor":"%ED%8A%B9%EC%A0%95-breakpoint-%EC%82%AD%EC%A0%9C"},{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"(lldb) br de 1"}]},{"anchor":"breakpoint-%EC%A0%84%EC%B2%B4-%EB%B9%84%ED%99%9C%EC%84%B1%ED%99%94","text":"breakpoint 전체 비활성화","level":3,"type":"heading"},{"type":"paragraph","inlineContent":[{"code":"(lldb) breakpoint disable","type":"codeVoice"},{"text":" ","type":"text"},{"type":"codeVoice","code":"(lldb) br di"}]},{"anchor":"%ED%8A%B9%EC%A0%95-breakpoint-%EB%B9%84%ED%99%9C%EC%84%B1%ED%99%94","level":3,"type":"heading","text":"특정 breakpoint 비활성화"},{"inlineContent":[{"type":"codeVoice","code":"(lldb) br di 1.1"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Thread","type":"text"},{"type":"text","text":" "},{"text":"스레드는 하나의 프로세스 내에서 실행되는 작업흐름의 단위를 말한다. 보통 한 프로세스는 하나의 스레드를 가지고 있지만 환경에 따라 둘 이상의 스레드를 동시에 실행할 수도 있다. 이러한 방식을 멀티스레딩이라고 한다. 프로그램 실행이 시작될 때부터 동작하는 스레드를 메인 스레드라고 하고 나중에 생성된 스레드를 서브 스레드 또는 세컨더리 스레드라고 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"프로세서"},{"type":"text","text":" "},{"text":"컴퓨터 내에서 프로그램을 수행하는 하드웨어 유닛으로 CPU(Central Processing Unit)가 여기에 속한다. 한 컴퓨터가 여러개의 프로세서를 갖는다면 멀티 프로세서라고 한다. (듀얼 프로세서 등)","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"코어"},{"type":"text","text":" "},{"type":"text","text":"프로세서 내부의 주요 연산회로를 말한다. 싱글코어는 하나의 연산회로가 내장되어 있는 것이고 듀얼코어는 두 개의 연산회로가 내장된 것을 말한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로그램과 프로세스"},{"text":" ","type":"text"},{"text":"프로그램은 보조기억 장치에 저장된 실행코드를 말한다. 프로세스는 이 프로그램을 구동하여 실행코드와 그 상태가 실제 메모리상에서 실행되는 작업 단위를 말한다. 동시에 여러개의 프로세스를 운용하는 시분할 방식을 멀티태스킹이라고 한다. 이러한 프로세스 관리는 운영체제에서 담당한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"비동기 프로그래밍"},{"text":" ","type":"text"},{"type":"text","text":"프로그램의 주 실행 흐름을 멈추어서 기다리지 않고 다음 작업을 실행할 수 있게 하는 방식이다. 코드의 실행 및 결과 처리를 별도의 공간에 맡겨둔 뒤 그 실행결과를 기다리지 않고 다음 코드를 실행하는 병렬처리 방식이다. 비동기 프로그래밍은 언어 및 프레임워크에서 지원하는 여러 방법으로 구현할 수 있다."}]}]},{"content":[{"inlineContent":[{"text":"동시성 프로그래밍","type":"text"},{"type":"text","text":" "},{"type":"text","text":"논리적인 용어로 동시에 실행되는 것처럼 보이는 방식이다. 싱글 코어에서 멀티스레드를 동작시키기 위한 방식으로 멀티 태스킹을 위해 여러 개의 스레드가 번갈아 가면서 실행되는 방식이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"병렬성 프로그래밍","type":"text"},{"type":"text","text":" "},{"text":"물리적으로 정확히 동시에 실행되는 것을 말한다. 멀티 코어에서 멀티 스레드를 동작시키는 방식으로 데이터 병렬성(Data Parallenlism)과 작업 병렬성(Task Parallelism)으로 구분된다.","type":"text"},{"text":" ","type":"text"},{"code":"데이터 병렬성","type":"codeVoice"},{"type":"text","text":" 전체 데이터를 나누어 서브 데이터들로 만든 뒤, 서브 데이터들을 병렬 처리해서 작업을 빠르게 수행하는 방법이다."},{"type":"text","text":" "},{"type":"codeVoice","code":"작업 병렬성"},{"text":" 서로 다른 작업을 병렬 처리하는 것을 말한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"동시성과 병렬성의 차이","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"동시성 프로그래밍과 병렬성 프로그래밍 모두 비동기 동작을 구현할 수 있지만 동작 원리가 다르다."},{"type":"text","text":" "},{"type":"text","text":"예를 들어 꼬치 가게에서 꼬치를 사려고 기다리고 있다고 생각해보자. 꼬치 가게 판매 직원이 2명이여서 사람들은 줄을 2줄로 섰다. 그래서 판매직원 한명이 한줄을 담당해서 N:N으로 업무처리를 진행하는 게 된다. 이게 병렬성이다. 병렬성은 물리적으로 동시에 여러작업을 처리할 수 있다. 판매직원이 하나의 코어가 되고, 줄이 처리해야하는 데이터나 작업인 것이다. 이 병렬성을 구현하기 위해서는 멀티 코어 환경이 필요하다."},{"text":" ","type":"text"},{"type":"text","text":"그렇다면 이 상황에서 판매직원 하나가 급하게 자리를 비운다면 어떻게 될까? 한 줄만 계속해서 판매된다면 다른 줄의 사람들의 원성을 사게 될 것이다. 그래서 한명의 판매직원이 두줄의 손님을 번갈아가면서 판매한다. 논리적으로는 하나의 코어가 여러줄을 작업을 동시에 처리하는 것처럼 보이지만 물리적으로 동시에 처리하는 것은 아니다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"iOS 환경에서의 동시성 프로그래밍 지원 종류"},{"text":" ","type":"text"},{"type":"codeVoice","code":"GCD (Grand Central Dispatch)"},{"type":"text","text":" 멀티 코어와 멀티 프로세싱 환경에서 최적화된 프로그래밍을 할 수 있도록 애플이 개발한 기술이다."},{"text":" ","type":"text"},{"type":"codeVoice","code":"Operation Queue"},{"type":"text","text":" 비동기적으로 실행되어야 하는 작업을 객체 지향적인 방법으로 사용한다"}],"type":"paragraph"}]}]},{"text":"Serial vs Concurrent","anchor":"Serial-vs-Concurrent","level":3,"type":"heading"},{"text":"직렬처리 vs 병렬처리","level":3,"anchor":"%EC%A7%81%EB%A0%AC%EC%B2%98%EB%A6%AC-vs-%EB%B3%91%EB%A0%AC%EC%B2%98%EB%A6%AC","type":"heading"},{"inlineContent":[{"text":"Serial인 경우 직렬이기때문에 앞에 작업이 있고 그 뒤에 작업이 있다면 앞의 작업이 끝나기 전까지는 뒤에 작업을 실행하지 않는다. 반대로 Concurrent인 경우는 병렬이기때문에 여러가지 작업을 동시에 실행할 수 있다. 즉, 조금이라도 먼저온 작업 순으로 바로바로 처리해줄 수 있다.","type":"text"}],"type":"paragraph"},{"text":"Async vs Sync","level":3,"anchor":"Async-vs-Sync","type":"heading"},{"level":3,"type":"heading","anchor":"%EB%B9%84%EB%8F%99%EA%B8%B0-vs-%EB%8F%99%EA%B8%B0","text":"비동기 vs 동기"},{"inlineContent":[{"text":"Async는 비동기다. 비동기란 내가 작업을 맡기고 실행되는 동안에 나는 또 다른 일을 할 수 있는 것을 뜻한다. 예를 들어 커피를 주문하고 기다리는 동안 아무것도 못하는게 아니라 다른일을 할 수 있는 그런 느낌이다. 반대로 Sync는 해당 작업이 끝날 때까지 기다려야 한다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Serial과 Concurrent는 "},{"code":"Thread 수와 관련이 있는 개념","type":"codeVoice"},{"text":"이고,","type":"text"},{"type":"text","text":" "},{"type":"text","text":"Async와 Sync는 "},{"code":"Thread 위에서의 흐름을 나타내는 개념","type":"codeVoice"},{"text":"이라고 생각하면 될 것 같다.","type":"text"}]},{"inlineContent":[{"text":"그리고 Cocoa Aplication에서는 2개의 Queue를 지원한다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Main","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Serial queue"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"반드시 UI 관련 작업(task)은 이곳에서 실행"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Global"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Concurrent queue (global dispatch queue)"}]}]},{"content":[{"inlineContent":[{"text":"동시에 하나 이상의 작업(task)을 실행","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"큐에 추가된 순서대로 시작한다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/yagom.net\/courses\/start-lldb\/"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/caution-dev.github.io\/ios\/2019\/03\/15\/iOS-GCD-vs-Operation-Queue.html","isActive":true,"type":"reference"}]}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Xcode"]]},"sections":[],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211015_LLDB,_GCD,_Thread,_Serial_Queue,_Async","interfaceLanguage":"swift"},"variants":[{"paths":["\/documentation\/arinote\/211015_lldb,_gcd,_thread,_serial_queue,_async"],"traits":[{"interfaceLanguage":"swift"}]}],"kind":"article","schemaVersion":{"minor":3,"major":0,"patch":0},"metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"title":"211015 LLDB, GCD, Thread, Serial Queue, Async","roleHeading":"Article"},"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://yagom.net/courses/start-lldb/":{"titleInlineContent":[{"text":"LLDB에 대한 강의","type":"text"}],"url":"https:\/\/yagom.net\/courses\/start-lldb\/","title":"LLDB에 대한 강의","type":"link","identifier":"https:\/\/yagom.net\/courses\/start-lldb\/"},"doc://AriNote/documentation/AriNote/Xcode":{"role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Xcode","url":"\/documentation\/arinote\/xcode","title":"Xcode","kind":"article","abstract":[{"text":"언젠가…. 좋아질거지 Xcode…?","type":"text"}],"type":"topic"},"https://caution-dev.github.io/ios/2019/03/15/iOS-GCD-vs-Operation-Queue.html":{"identifier":"https:\/\/caution-dev.github.io\/ios\/2019\/03\/15\/iOS-GCD-vs-Operation-Queue.html","title":"GCd vs Operation Queue","titleInlineContent":[{"text":"GCd vs Operation Queue","type":"text"}],"type":"link","url":"https:\/\/caution-dev.github.io\/ios\/2019\/03\/15\/iOS-GCD-vs-Operation-Queue.html"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211018_let,_var,_tool_bar,_design_patterns,_architectures,_mvc,_dictionary,_downcasting,_function_types_as_parameter_types.json b/docs/data/documentation/arinote/211018_let,_var,_tool_bar,_design_patterns,_architectures,_mvc,_dictionary,_downcasting,_function_types_as_parameter_types.json new file mode 100644 index 0000000..9d7f486 --- /dev/null +++ b/docs/data/documentation/arinote/211018_let,_var,_tool_bar,_design_patterns,_architectures,_mvc,_dictionary,_downcasting,_function_types_as_parameter_types.json @@ -0,0 +1 @@ +{"schemaVersion":{"minor":3,"major":0,"patch":0},"primaryContentSections":[{"content":[{"anchor":"TIL-Today-I-Learned","level":1,"type":"heading","text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"type":"text","text":"10월 18일 (월)"}]},{"level":2,"text":"학습 내용","type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"paragraph","inlineContent":[{"text":"오늘은 활동학습으로 카훗을 시작으로 디자인패턴, 아키텍처, MVC에 대한 내용으로 학습을 진행하였다. 일요일 저녁에 미리 예습을 하고 가서 학습을 이해하는 것에 대해서 큰 도움이 되긴 했다. 그래도 역시 뭔말인지 모르는 포인트가 뜨문뜨문 있어서 복습이 필요해보였다. 이후 새로운 프로젝트 팀원인 제이티와 함께 그라운드 룰과 STEP 1을 구현하기 시작했다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"heading","text":"문제점 \/ 고민한 점","level":2,"anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"화면 하단에 액션을 모아둔 바의 이름…몰라"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Swift가 var보다 let을 선호하는 이유? 나 아는데… 뭐였지?"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"디자인 패턴이란?","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"디자인 패턴 강의를 듣다가 책을 추천받았다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Design Patterns vs Architectures"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"타입을 구현하기 전에 설계를 어떻게 진행할 것인가?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"과일을 초기화 할 때 모든 과일을 한꺼번에 초기화 하는 법","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정해진 레시피를 어떻게 정의를 해주면 좋을까?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"과일 재고를 관리할 때 연산자로 더하고 빼줄 수는 없을까?"}]}]},{"content":[{"inlineContent":[{"text":"모든 과일을 초기화하는 것이아니라 개별적으로도 초기화 할 수 있는 방법을 찾아보고 싶다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"에러처리시 케이스별로 메세지를 출력하는 법에 대한 고민"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","type":"heading","text":"해결방법","level":2},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"화면 하단에 액션을 모아둔 바의 이름은 "},{"code":"Tool bar","type":"codeVoice"},{"text":"이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Swift가 var 및 let을 사용하는 것을 귀찮게 참견하는 이유?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"상수(let)는 변경할 필요가 없는 값으로 코드를 보다 안전하고 명확하게 하기 위해 Swift는 전반적으로 상수가 사용된다. 이는 혹시나 나중에 실수로 값을 변경하지 않도록 하는데 좋다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"let은 프로그램이 실행이 시작되기 전에 예약된다. 10개의 상수와 10개의 변수가 있는 프로그램이 있는 경우 10개의 상수에 대한 메모리가 할당되고 초기화 된다. (할당된 값은 할당된 메모리에 저장됨) 상수가 많을수록 메모리 공간이 늘어나는데, 값이 할당되면 변경되지 않으므로 실행시 시간을 절약할 수 있다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"객체지향의 사실과 오해"},{"text":" 구매하였다. 시간날 때 마다 정독해야지.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"Design Patterns","type":"codeVoice"},{"type":"text","text":" vs "},{"type":"codeVoice","code":"Architectures"}]},{"items":[{"content":[{"inlineContent":[{"inlineContent":[{"type":"text","text":"Architecture"}],"type":"strong"},{"type":"text","text":" "},{"text":"큰 그림, 소프트웨어의 전반적인 큰 그림, 프로그램의 구조","type":"text"},{"type":"text","text":" "},{"text":"ex) 서양 건축물 양식, MVC architecture","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"inlineContent":[{"type":"text","text":"Design Patten"}],"type":"strong"},{"text":" ","type":"text"},{"text":"이 아키텍처 안에서 세세한 부분을 해결하는 해결방식","type":"text"},{"type":"text","text":" "},{"type":"text","text":"ex) 서양 건축물 안의 불편한 설계 부분을 수정해서 현재에 맞게 적용하는 것"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"제이티와 함께 ","type":"text"},{"type":"codeVoice","code":"순서도"},{"text":"를 그려볼까…? 했었는데 모든 스텝이 오픈 되지가 않아서 그리기가 애매했다. 일단 ","type":"text"},{"inlineContent":[{"type":"text","text":"STEP 1에 대한 요구사항을 충족"}],"type":"strong"},{"type":"text","text":"하도록 구현하기로 하였고, 노션으로 같이 메모해가면서 "},{"type":"codeVoice","code":"FruitStore"},{"text":"와 ","type":"text"},{"code":"JuiceMaker","type":"codeVoice"},{"type":"text","text":"의 타입을 만들고 기능을 구현하는 것에 힘썼다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"과일의 초기값을 정해서 초기화하는 부분에 대해서 나에게 좋은 생각이 있어서 제이티에게 제안을 했다. 우선 과일과 과일의 갯수를 저장하는 컬렉션으로 "},{"type":"codeVoice","code":"Dictionary"},{"type":"text","text":"가 적합하다고 의견을 나누었고, Dictionary "},{"code":"이니셜라이저","type":"codeVoice"},{"text":" 중에 튜플을 받아서 딕셔너리로 초기화 해주는 이니셜라이저가 있어서 찾아보았고, 잘 활용해서 구현해보았다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"code":"Dictionary init(uniqueKeysWithValues:)","type":"codeVoice"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정해진 레시피를 받아서 과일의 재고를 관리해주어야 하는 부분이 있었는데, 레시피를 어떤식으로 받아올지 좋은 생각이 안났다. 제이티가 Dictionary로 통해서 받는 기발한 아이디어를 떠올라서 활용해보았다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"연산자로 더하고 빼는 방법 역시 제이티의 아이디어였는데, 바로 ","type":"text"},{"type":"strong","inlineContent":[{"text":"파라미터로 함수를 받아서 활용하는 방법","type":"text"}]},{"type":"text","text":"이였다."}]}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift=","code":["var num = 3","func operate(operNum: Int, fun: (Int, Int) -> Int) {"," num = fun(num, operNum)","}","operate(operNum: 6, fun: +)","print(num) \/\/ 9"]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"과일을 개별적으로 초기화하라는 요구사항은 없지만 궁금해서 구현해보기로 하였다. 이니셜라이저를 다시 복습하고 만들어보기로…"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"원래는 "},{"code":"enum","type":"codeVoice"},{"text":" 내부에서 ","type":"text"},{"code":"static","type":"codeVoice"},{"text":" 메서드를 구현하여 출력해주기로 하였으나, 꼭 ","type":"text"},{"type":"codeVoice","code":"static"},{"type":"text","text":" 메서드로 구현해주지 않아도 "},{"type":"codeVoice","code":"Error"},{"type":"text","text":" 프로토콜을 채택하고 있기 때문에 다운캐스팅("},{"code":"as","type":"codeVoice"},{"type":"text","text":")을 통해서 메서드를 호출해줄 수 있었다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"codeListing","code":["enum RequestError: Error {"," case wrongInput"," case notFound"," case fruitStockOut"," "," func printErrorMessage() {"," switch self {"," case RequestError.wrongInput:"," print(\"수량을 잘못 입력하였습니다.\")"," case RequestError.notFound:"," print(\"선택한 과일이 존재하지 않습니다.\")"," case RequestError.fruitStockOut:"," print(\"과일의 재고가 부족합니다.\")"," }"," }","}","do {"," try maker.fruitsMixer(juice: .strawberryBanana)","} catch let error as RequestError {"," error.printErrorMessage()","}"],"syntax":"swift="},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"text":"공부내용 정리","type":"heading","anchor":"%EA%B3%B5%EB%B6%80%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC","level":2},{"anchor":"Design-Pattern","type":"heading","text":"Design Pattern","level":1},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"설계할 때 자주 쓰이는 템플릿"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"선배들의 삽질 기록"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"코드의 모양새","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"디자인 패턴은 소프트웨어 공학의 소프트웨어 설계에서 공통으로 발생하는 문제에 대해 자주 쓰이는 설계 방법을 정리한 패턴이다."}],"type":"paragraph"}]}]},{"level":2,"text":"사용 이유","type":"heading","anchor":"%EC%82%AC%EC%9A%A9-%EC%9D%B4%EC%9C%A0"},{"inlineContent":[{"text":"디자인 패턴을 참고하여 개발할 경우 효율성과 유지보수성, 운용성이 높아지며 프로그램의 최적화에 도움이 된다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"OOP의 다양한 문제 상황에 대한 예방","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"프로그래머 사이의 협업 효율 향상"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로그래머 사이의 의사소통 증진"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"코드의 안정화 및 최적화","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"코드의 재사용성 증가","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":"주어진 패턴을 상황에 맞게 변경을 해서 사용해야 하는데 디자인 패턴에 집착하게 되면 유연하게 패턴을 적용 및 변경을 못하게 된다. 따라서 100퍼센트 지킬 필요는 없지만 명확하게 알아두면 쓸 일이 많다."}]},{"type":"heading","text":"Architectures","level":1,"anchor":"Architectures"},{"inlineContent":[{"type":"text","text":"간단하게 프로그램의 구조라고 생각한다. 위키에서는 소프트웨어 내에서의 공통적인 발생 문제들을 해결하기 위한 일반적인 해결 방법이라고 설명하고 있다."}],"type":"paragraph"},{"level":2,"text":"사용 이유","anchor":"%EC%82%AC%EC%9A%A9-%EC%9D%B4%EC%9C%A0","type":"heading"},{"inlineContent":[{"type":"text","text":"프로그램은 제대로 작성만 된다면 실행이 가능하다. 하지만 이런 프로그램들은 유지보수에 굉장히 많은 비용이 들어가며 실력있는 개발자가 보기에는 가독성이 떨어진다고 볼 수 있다."}],"type":"paragraph"},{"type":"heading","text":"좋은 아키텍처란?","level":2,"anchor":"%EC%A2%8B%EC%9D%80-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EB%9E%80"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"균형잡힌 분배(Balanced Distribution)"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"객체 지향 원칙 중 Single Responsibility에 기반 > 하나의 객체는 하나의 역할만 갖는다","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"모듈(클래스)들의 독립성이 떨어지면 테스트가 어렵다","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"테스트 가능 (Testablity)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"테스트 중 발생하는 이슈를 사전에 발견하기 위한 단계다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"사용하기 쉬운지 (Easy of Use)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"개발 속도와 관련이 있다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"단방향성 데이터 흐름(Unidirectional Data Flow)","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"코드를 쉽게 이해할 수 있게 하며 쉬운 디버깅을 제공한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"에러가 발생하면 원인을 찾기 힘들어 지는 공유 자원의 사용도 피해야 한다."}]}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"text":"4가지 조건을 충족 시키는 완벽한 아키텍처는 존재하지 않는다. 그러니깐 자신의 프로젝트 성격에 맞춰서 적절한 아키텍처 도입이 필요하다.","type":"text"}]},{"inlineContent":[{"text":"iOS에서는 4가지 조건 중 균형잡힌 분배를 위해서 크게 3가지로 나누어 코딩이 진행된다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Model
데이터 조작이 일어나고 이를 담당하는 부분","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"View
사용자에게 보여주는 시각적인 부분으로 UI에 해당.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Controller \/ Presenter \/ ViewModel
이 부분은 Model과 View 사이의 중재자로 View를 통해 발생한 사용자의 액션에 따라 동작하며 Model에 값의 조정을 요청하며 Model 값의 변화에 맞게 View를 갱신하는 역할"},{"type":"text","text":" "},{"type":"text","text":"그래서 iOS에서 가장 많이 사용되는 아키텍처 패턴인 MVC, MVP, MVVM가 있다."}],"type":"paragraph"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Architecture"},{"type":"text","text":" "},{"text":"큰 그림, 소프트웨어의 전반적인 큰 그림, 프로그램의 구조","type":"text"},{"type":"text","text":" "},{"type":"text","text":"ex) 서양 건축물 양식, MVC architecture"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Design Patten"},{"type":"text","text":" "},{"type":"text","text":"이 아키텍처 안에서 세세한 부분을 해결하는 해결방식"},{"type":"text","text":" "},{"type":"text","text":"ex) 서양 건축물 안의 불편한 설계 부분을 수정해서 현재에 맞게 적용하는 것"}]}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/nncCDHO.gif"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Xcode 프로젝트 디렉토리에서 Model, View, Controller 폴더를 따로 만들어서 관리한다."}]},{"type":"heading","level":2,"text":"Model","anchor":"Model"},{"inlineContent":[{"type":"text","text":"앱이 정확히 무엇을 할지 코딩하는 것이다. 비즈니스 로직을 담당하는 함수들이 정의되고, 처리되는 데이터(클래스, 구조체 등)와 내부 알고리즘이 정의된다."}],"type":"paragraph"},{"text":"View","type":"heading","level":2,"anchor":"View"},{"inlineContent":[{"text":"사용자에게 말 그대로 보여지는 영역으로 볼 수 있다. Storyboard 파일을 비롯해서 인터페이스를 구축하는 영역으로 생각하면 될 것 같다.","type":"text"}],"type":"paragraph"},{"text":"Controller","level":2,"type":"heading","anchor":"Controller"},{"type":"paragraph","inlineContent":[{"text":"Model 과 View 사이의 다리라고 보면 된다. Controller는 Model이 가지고 있는 데이터를 어떻게 할 것인지 명령을 내린다. 그리고 이 명령을 토대로 사용자에게 보여지는 인터페이스 부분도 수정을 한다. 보통 여기서 @IBAction 함수들이 정의된다. 즉 사용자가 View를 통해 Interaction을 하면 Controller가 이를 control한다는 것이다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"text":"Model 에서는 비즈니스 로직을, View에서는 사용자에게 보여지는 것들을, 그리고 Controller 에서는 어떻게 Model 을 활용해서 View 에게 보여질 것인지만 정의하면 되니, 코드가 간결해지고, 관리가 용이해진다.","type":"text"}]},{"type":"heading","level":2,"text":"MVC 패턴에서 각 영역이 대화하는 방법","anchor":"MVC-%ED%8C%A8%ED%84%B4%EC%97%90%EC%84%9C-%EA%B0%81-%EC%98%81%EC%97%AD%EC%9D%B4-%EB%8C%80%ED%99%94%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95"},{"inlineContent":[{"type":"text","text":"Controller는 Model과 View에 직접 지시를 할 수 있지만 Model과 View는 Controller에 직접적으로 알릴 수 없다."},{"type":"text","text":" "},{"type":"text","text":"그렇다면 만약 Model의 데이터가 변경된 것을 알리거나, View에서 사용자의 action이 발생했을 때 Controller에게 어떻게 알릴까?"}],"type":"paragraph"},{"level":2,"type":"heading","text":"View to Controller","anchor":"View-to-Controller"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/ypG6NxM.jpg"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"컨트롤러는 View에 대해서 outlet을 이용해서 View에게 직접 접근할 수 있다. View는 target - Action 구조로 사용자의 행위에 따라 필요한 함수를 호출 할 수 있다. 또한 구조적으로 미리 정해진 방식으로 행위에 대한 요청 (delegate), 데이터에 대한 요청(data-source)을 할 수 있다.","type":"text"}]},{"anchor":"Model-to-Controller","level":2,"text":"Model to Controller","type":"heading"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/w3KNBvI.jpg"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"컨트롤러는 모델에 접근할 수 있다. 하지만 모델은 Notification & KVO 방식을 통해 모델의 변화를 컨트롤러에게 알릴 수 있다."}],"type":"paragraph"},{"type":"heading","level":2,"anchor":"%EC%9E%A5%EC%A0%90","text":"장점"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"다른 패턴에 비해 코드량이 적다"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"애플에서 기본적으로 지원하고 있는 패턴이기 때문에 쉽게 접근할 수 있다"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"많은 개발자들에게 친숙한 패턴이기 때문에 개발자들이 쉽게 유지보수 할 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"개발 속도가 빠르기 때문에 아키텍처가 중요하지 않을 때 사용하거나 규모가 작은 프로젝트에서 사용하기 좋다."}]}]}]},{"type":"heading","anchor":"%EB%8B%A8%EC%A0%90","text":"단점","level":2},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/pQFMobW.png","type":"image"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"위의 사진과 같이 뷰와 컨트롤러가 너무 밀접하게 연결되어 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"View와 Controller가 붙어있으며 Controller가 View의 Lift Cycle까지 관리하기 때문에 View와 Controller를 분리하기 어렵다. 이렇게 되면 재사용성이 떨어지고 유닛 테스트를 진행하기 힘들어진다."}]}]},{"content":[{"inlineContent":[{"text":"대부분의 코드가 Controller에 밀집될 수 있다. Life Cycle 관리 뿐만 아니라 delegate나 datasource관리, 네트워크 요청, DB에 데이터 요청 등 많은 코드가 Controller에 작성되면 Controller의 크기는 비대해지고 내부 구조는 복잡해지게 된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"이런 상황을 비유해 많은 사람들이 Massive View Contorller라고 부르기도 한다"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이렇게 복잡해진 코드는 프로젝트 규모가 커질수록 유지보수하기 힘들게 만든다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"heading","level":2,"anchor":"MVC-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EB%8A%94-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98%EC%9D%98-%EA%B8%B0%EC%A4%80%EC%97%90-%EC%96%BC%EB%A7%88%EB%82%98-%EB%B6%80%ED%95%A9%ED%95%A0%EA%B9%8C","text":"MVC 아키텍처는 아키텍처의 기준에 얼마나 부합할까?"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Distribution
View와 Model은 분리되었지만 View와 Controller는 강하게 연결되어 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Testability
View와 Controller가 강하게 연결되어 있어서 Model만 테스트를 진행 할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Easy of Use
가장 적은 양의 코드를 필요하며 경험이 적은 개발자들도 쉽게 유지보수 할 수 있다."}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":"아키텍처를 잘 모를 때 사용하기 쉬운 패턴이지만 작은 프로젝트여도 많은 유지보수 비용이 들어간다."}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","isActive":true,"identifier":"https:\/\/ko.wikipedia.org\/wiki\/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4_%EB%94%94%EC%9E%90%EC%9D%B8_%ED%8C%A8%ED%84%B4"}]}]},{"content":[{"inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/en.wikipedia.org\/wiki\/Architectural_pattern"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/www.swiftbysundell.com\/articles\/model-controllers-in-swift\/"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/medium.com\/ios-os-x-development\/ios-architecture-patterns-ecba4c38de52","isActive":true,"type":"reference"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}],"kind":"content"}],"sections":[],"metadata":{"role":"article","roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"title":"211018 let, var, tool bar, Design Patterns, Architectures, MVC, Dictionary, Downcasting, Function Types as Parameter Types"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211018_let,_var,_tool_bar,_design_patterns,_architectures,_mvc,_dictionary,_downcasting,_function_types_as_parameter_types"]}],"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211018_let,_var,_tool_bar,_Design_Patterns,_Architectures,_MVC,_Dictionary,_Downcasting,_Function_Types_as_Parameter_Types","interfaceLanguage":"swift"},"references":{"https://medium.com/ios-os-x-development/ios-architecture-patterns-ecba4c38de52":{"identifier":"https:\/\/medium.com\/ios-os-x-development\/ios-architecture-patterns-ecba4c38de52","title":"iOS Architecture Patterns","type":"link","titleInlineContent":[{"text":"iOS Architecture Patterns","type":"text"}],"url":"https:\/\/medium.com\/ios-os-x-development\/ios-architecture-patterns-ecba4c38de52"},"https://en.wikipedia.org/wiki/Architectural_pattern":{"identifier":"https:\/\/en.wikipedia.org\/wiki\/Architectural_pattern","title":"Architectural","type":"link","titleInlineContent":[{"text":"Architectural","type":"text"}],"url":"https:\/\/en.wikipedia.org\/wiki\/Architectural_pattern"},"https://www.swiftbysundell.com/articles/model-controllers-in-swift/":{"identifier":"https:\/\/www.swiftbysundell.com\/articles\/model-controllers-in-swift\/","title":"Model만을 다루는 Model Controller를 따로 만들어 사용하는 방법","type":"link","titleInlineContent":[{"text":"Model만을 다루는 Model Controller를 따로 만들어 사용하는 방법","type":"text"}],"url":"https:\/\/www.swiftbysundell.com\/articles\/model-controllers-in-swift\/"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/ypG6NxM.jpg":{"identifier":"https:\/\/i.imgur.com\/ypG6NxM.jpg","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/ypG6NxM.jpg"}]},"https://i.imgur.com/pQFMobW.png":{"identifier":"https:\/\/i.imgur.com\/pQFMobW.png","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/pQFMobW.png"}]},"https://i.imgur.com/w3KNBvI.jpg":{"identifier":"https:\/\/i.imgur.com\/w3KNBvI.jpg","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/w3KNBvI.jpg"}]},"https://i.imgur.com/nncCDHO.gif":{"identifier":"https:\/\/i.imgur.com\/nncCDHO.gif","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/nncCDHO.gif"}]},"https://ko.wikipedia.org/wiki/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4_%EB%94%94%EC%9E%90%EC%9D%B8_%ED%8C%A8%ED%84%B4":{"identifier":"https:\/\/ko.wikipedia.org\/wiki\/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4_%EB%94%94%EC%9E%90%EC%9D%B8_%ED%8C%A8%ED%84%B4","title":"Design Pattern","type":"link","titleInlineContent":[{"text":"Design Pattern","type":"text"}],"url":"https:\/\/ko.wikipedia.org\/wiki\/%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4_%EB%94%94%EC%9E%90%EC%9D%B8_%ED%8C%A8%ED%84%B4"},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211019_nested_type,_operator,_dictionary,_sequence,_localizederror,_error_handling.json b/docs/data/documentation/arinote/211019_nested_type,_operator,_dictionary,_sequence,_localizederror,_error_handling.json new file mode 100644 index 0000000..7f285b0 --- /dev/null +++ b/docs/data/documentation/arinote/211019_nested_type,_operator,_dictionary,_sequence,_localizederror,_error_handling.json @@ -0,0 +1 @@ +{"kind":"article","sections":[],"schemaVersion":{"minor":3,"major":0,"patch":0},"primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","level":1,"text":"TIL (Today I Learned)","type":"heading"},{"inlineContent":[{"type":"text","text":"10월 19일 (화)"}],"type":"paragraph"},{"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용","level":2},{"inlineContent":[{"text":"오늘은 제이티와 STEP 1에 대한 코드를 리팩토링 하기로 하였다. 잠깐 코드를 보면서 생각해봤던 부분을 나누었고, README와 PR내용을 같이 적어보면서 고민했던 점과 의문점, 조언을 얻고 싶은 점을 정리해보면서 애매하게 알고있는 개념들을 다시 같이 공부해보기도 하면서 시간을 보냈다. 같이 글로 정리하면서 느낀 것은 코드를 작성할 때는 대충 감으로 지식을 아는 느낌이였다면 말로 설명하려고 하거나 글로 정리하려니 정확한 내용을 전달해야해서 정확한 근거를 찾게 된다는 것이다. 글로 정리하면서 대충 알고 있던 지식들을 다시한번 복습하게 되면서 정확히 알아가던 유익한 시간이였다. 이후 README 작성을 마치고 PR을 보낸 후 각자 피드백이 올 때 까지 개인 공부 시간을 가지기로 하고 마무리 했다.","type":"text"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","type":"heading","text":"문제점 \/ 고민한 점","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"과일 재고를 관리하는 메서드가 너무 low한 것 같다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"초기값을 전역변수로 선언해주면 어떨까?"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"파라미터는 함수타입인데, 왜 연산자만 넣어도 잘 작동할까?","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Nested Type을 이용하여 연관된 타입을 내부에 작성해주었는데, 이게 가독성 측면에서 좋은걸까?","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"예외처리를 구현해주었는데, do-catch문을 사용하지 않았다. 이유는 에러처리는 그것을 사용하는 공간에서 해야된다고 생각했기 때문인데, Controller에서 사용할 때 구현해주려 했다. 근데 Model에서 미리 구현해놔야 하는 건지 헷갈리기 시작했다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Dictionary의 init(uniqueKeysWithValues:)는 Sequence를 두개를 받아 초기화를 하는데, Array도 들어가네…? Array도 Sequence인가? Sequence가 정확히 뭘까?","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"LocalizedError 프로토콜을 알게되었다. 쓰임새가 궁금해졌다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"text":"해결방법","level":2,"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"과일 재고를 관리하는 메서드를 이용하여 명확하게 add,sub로 나누어 메서드를 별도로 구현해주었다."}]}]},{"content":[{"inlineContent":[{"text":"전역변수로 선언해주고 private 키워드를 주었다. let으로 상수이기 때문에 전역변수도 괜찮다고 생각이 들었는데, 리뷰어분께도 조언을 들어보기로 하였다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"swift integer operators를 검색해서 타고 들어가니 연산자가 어떻게 구성되어있는지 나와있었다. 연산자도 함수형태이기 때문에 파라미터로 전달하여도 문제없었던게 맞았다! 어느정도 예측은 하고있었으나 근거를 찾고 싶었다. 🤣","type":"text"}]}]}]},{"type":"codeListing","syntax":"swift=","code":["static func + (lhs: Int, rhs: Int) -> Int"]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"연관된 타입을 정의된 타입(구조체나 클래스) 내부에서만 사용한다면 문제없겠지만 만약 외부에서도 널리 사용하게 된다면 밖으로 빼주는 것이 맞을 것 같다는 생각이 들었다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"리뷰어인 흰에게도 물어보고, 2기, 3기 선배분들 한태도 물어보았다. 결론은 정답은 없다. 그 문제를 해결하려고 MVC, MVVM, Viper 등의 아키텍처가 나왔는데, 여전히 뭐가 더 낫니 아니니 말이 많기 때문이다. 그러나 MVC 관점에서 보면 컨트롤러가 에러 핸들링을 하는게 적절하다. 다만 massive view controller라는 문제도 있으니 알고 지나가면 좋을 것 같다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Array는 Sequence 프로토콜을 기반으로 작성되었다는 사실을 알게되었다. 그래서 Array 타입을 사용할 때 Sequence의 대부분의 기능을 제공해준다. map, filter뿐만 아니라 Sequence 안에서 특정 조건을 만족하는 첫번째 요소를 찾는 기능 까지 모두 다 Sequence 프로토콜 안에 정의되어 있다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"Sequence는 두가지 중요한 특징이 있는데 무한하거나, 유한하다. 그리고 한번만 이터레이트(iterate)할 수 있다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"LocalizedError는 오류와 오류가 발생한 이유를 설명하는 메세지를 제공하는 프로토콜이다. 프로퍼티가 모두 Optional 형태로 정의있어서 구현해도 되고 안해도된다. 어떤 역할을 하는 지는 이름이 직관적이라서 이름만 봐도 어떤 역할인지 알 수 있었다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":" "}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/int\/2885932","isActive":true,"type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/NestedTypes.html","isActive":true}]}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/swift\/dictionary\/3127165-init"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/sequence","type":"reference","isActive":true}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"reference","isActive":true,"identifier":"https:\/\/academy.realm.io\/kr\/posts\/try-swift-soroush-khanlou-sequence-collection\/"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/localizederror"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211019_nested_type,_operator,_dictionary,_sequence,_localizederror,_error_handling"]}],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211019_Nested_Type,_operator,_Dictionary,_Sequence,_LocalizedError,_Error_Handling"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","title":"211019 Nested Type, operator, Dictionary, Sequence, LocalizedError, Error Handling"},"references":{"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://docs.swift.org/swift-book/LanguageGuide/NestedTypes.html":{"title":"Nested Types","titleInlineContent":[{"text":"Nested Types","type":"text"}],"identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/NestedTypes.html","url":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/NestedTypes.html","type":"link"},"https://developer.apple.com/documentation/foundation/localizederror":{"type":"link","titleInlineContent":[{"type":"text","text":"protocol LocalizedError"}],"identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/localizederror","url":"https:\/\/developer.apple.com\/documentation\/foundation\/localizederror","title":"protocol LocalizedError"},"https://developer.apple.com/documentation/swift/int/2885932":{"title":"Swift integer operator +","titleInlineContent":[{"text":"Swift integer operator +","type":"text"}],"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/int\/2885932","url":"https:\/\/developer.apple.com\/documentation\/swift\/int\/2885932","type":"link"},"https://developer.apple.com/documentation/swift/dictionary/3127165-init":{"type":"link","titleInlineContent":[{"type":"text","text":"Dictionary uniquekeyswithvalues"}],"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/dictionary\/3127165-init","url":"https:\/\/developer.apple.com\/documentation\/swift\/dictionary\/3127165-init","title":"Dictionary uniquekeyswithvalues"},"https://developer.apple.com/documentation/swift/sequence":{"title":"Sequence","titleInlineContent":[{"text":"Sequence","type":"text"}],"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/sequence","url":"https:\/\/developer.apple.com\/documentation\/swift\/sequence","type":"link"},"https://academy.realm.io/kr/posts/try-swift-soroush-khanlou-sequence-collection/":{"type":"link","titleInlineContent":[{"type":"text","text":"Swift의 Sequence와 Collection"}],"identifier":"https:\/\/academy.realm.io\/kr\/posts\/try-swift-soroush-khanlou-sequence-collection\/","url":"https:\/\/academy.realm.io\/kr\/posts\/try-swift-soroush-khanlou-sequence-collection\/","title":"Swift의 Sequence와 Collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211020_kvc,_notification,_notificationcenter.json b/docs/data/documentation/arinote/211020_kvc,_notification,_notificationcenter.json new file mode 100644 index 0000000..f01e5f8 --- /dev/null +++ b/docs/data/documentation/arinote/211020_kvc,_notification,_notificationcenter.json @@ -0,0 +1 @@ +{"schemaVersion":{"patch":0,"major":0,"minor":3},"primaryContentSections":[{"content":[{"type":"heading","text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned","level":1},{"inlineContent":[{"text":"10월 20일 (수)","type":"text"}],"type":"paragraph"},{"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용","level":2},{"type":"paragraph","inlineContent":[{"text":"오늘은 내일 활동학습을 위한 예습을 해보았다. Notification을 학습하는 과정중에 이해가 너무 안되서 예제코드를 찾아보면서 이해하려고 노력했다. KVO는 이전에 ","type":"text"},{"isActive":true,"identifier":"https:\/\/github.com\/leeari95\/TIL\/blob\/main\/2021-10\/211012%20%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%2C%20Properties%2C%20KeyPath%2C%20KVC%2C%20iOS%2C%20HIG.md","type":"reference"},{"text":"를 학습하면서 배웠던 내용이랑 비슷하여 어렵진 않았다.","type":"text"}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"level":3,"anchor":"KVO-Notification%EB%93%A4-%EA%B0%99%EC%9D%80-%ED%8C%A8%ED%84%B4%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0","type":"heading","text":"KVO, Notification들 같은 패턴을 사용하는 이유?"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"KVO와 Notification은 인스턴스 간의 통신을 하기 위한 수단이다. 통신을 한다는 것은 데이터, 정보를 주고받는 것이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하나의 객체가 다른 객체와 "},{"type":"strong","inlineContent":[{"type":"text","text":"소통"}]},{"text":"은 하지만 ","type":"text"},{"type":"strong","inlineContent":[{"type":"text","text":"묶이기(coupled)는 싫을 때"}]},{"type":"text","text":" "},{"type":"codeVoice","code":"KVO"},{"text":", ","type":"text"},{"code":"Notification","type":"codeVoice"},{"text":", ","type":"text"},{"type":"codeVoice","code":"Observing"},{"text":" 등을 사용한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"두 패턴 모두 특정 이벤트가 일어나면 원하는 객체에 알려주어 해당되는 처리를 하는 방법을 가지고 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"어플리케이션의 특성상 객체간 소통은 필수적이다. 하지만 한 객체는 그 자체로 존재하면서 소통하고 싶을 뿐 다른 객체에 종속되어 동작하는 것은 ‘재사용성’과 ‘독립된 기능 요소’측면에서 볼 때 바람직하지 않다는 것이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"각 타입들끼리 의존하지 않고 서로 연결되지 않은 채 역할을 분담하는 것이 유지 보수와 수정에 용이하다.","type":"text"}],"type":"paragraph"}]}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"level":2,"type":"heading","text":"문제점 \/ 고민한 점","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"KVO는 알겠는데, Notification에 대한 이해가 어려웠다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":" "}]}]}]},{"type":"heading","text":"해결방법","anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","level":2},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"직접 예제코드를 작성해보면서 이해해보았다."},{"type":"text","text":" "},{"type":"text","text":" "}],"type":"paragraph"}]}]},{"type":"heading","text":"공부내용 정리","anchor":"%EA%B3%B5%EB%B6%80%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC","level":2},{"inlineContent":[{"type":"text","text":"KVO는 A객체에서 B객체의 프로퍼티가 변화됨을 감지할 수 있는 패턴이다. Notification이 주로 Controller와 다른 객체 사이의 관계를 다룬다면 KVO는 객체와 객체 사이의 관계를 다루는데 적합하다. 메소드나 다른 액션에서 나타나는 것이 아니라 프로퍼티의 상태에 반응하는 형태이다."}],"type":"paragraph"},{"type":"heading","anchor":"%EC%9E%A5%EC%A0%90","text":"장점","level":1},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"두 객체 사이의 정보를 맞춰주는 것이 쉽다"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"New \/ old value를 쉽게 얻을 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"KeyPath로 옵저빙하기 때문에 nested objects도 옵저빙이 가능하다.","type":"text"}]}]}],"type":"unorderedList"},{"level":1,"text":"단점","type":"heading","anchor":"%EB%8B%A8%EC%A0%90"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"NSobject를 상속받는 객체에서만 사용이 가능하다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"dealloc될 때 옵저버를 지워줘야 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"많은 value를 감지할 때는 많은 조건문이 필요하다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"객체의 프로퍼티의 변경사항을 다른 객체에 알리기 위해 사용하는 코코아 프로그래밍 패턴","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Model과 View와 같이 논리적으로 분리된 파트간의 변경사항을 전달하는데 유용함"}]}]},{"content":[{"inlineContent":[{"text":"NSObject를 상속한 클래스에서만 KVO를 사용할 수 있다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"text":"Observing을 위한 Setup","level":1,"type":"heading","anchor":"Observing%EC%9D%84-%EC%9C%84%ED%95%9C-Setup"},{"code":["class Person: NSObject {"," let name: String"," @objc dynamic var age: Int"," "," init(name: String, age: Int) {"," self.name = name"," self.age = age"," }","}"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"age가 변경하는 걸 다른 객체에게 알리고 싶다면 위 예제처럼 NSObject 상속을 받아야한다.","type":"text"},{"text":" ","type":"text"},{"text":"SObject를 상속한 클래스에서만 KVO를 사용할 수 있기 때문이다.","type":"text"},{"text":" ","type":"text"},{"text":"상속을 해야하므로 class에서만 사용가능하다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"observe하려는 프로퍼티에 @objc attribute와 dynamic modifier를 추가해야한다.","type":"text"}]}]}],"type":"orderedList"},{"type":"thematicBreak"},{"type":"heading","level":1,"text":"Observer 정의","anchor":"Observer-%EC%A0%95%EC%9D%98"},{"inlineContent":[{"type":"text","text":"KeyPath를 사용하여 프로퍼티 KeyPath에 observer를 추가할 수 있다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"관찰자 클래스의 인스턴스는 하나 이상의 속성에 대한 변경사항에 대한 정보를 관리한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"관찰자를 만들 때 관찰하려는 속성을 참조하는 키 경로로 메서드를 호출하여 관찰을 시작한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"syntax":"swift","code":["var person = Person(name: \"ari\", age: 20)","","person.observe(\\.age , options: [.old, .new]) { (object, change) in"," print(\"갑자기 \\(object.name)의 나이(\\(change.oldValue!))가 \\(change.newValue!)살이 되어버렸다...\")","}","person.age = 24 \/\/ 갑자기 ari의 나이(20)가 24살이 되어버렸다...","person.age = 27 \/\/갑자기 ari의 나이(24)가 27살이 되어버렸다..."],"type":"codeListing"},{"type":"heading","text":"프로퍼티 옵저버와 다른점이 뭘까?","anchor":"%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EC%98%B5%EC%A0%80%EB%B2%84%EC%99%80-%EB%8B%A4%EB%A5%B8%EC%A0%90%EC%9D%B4-%EB%AD%98%EA%B9%8C","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로퍼티 옵저버는 타입 정의 내부에 위치"},{"type":"text","text":" "},{"type":"text","text":"KVO는 타입 정의 외부에서 observer를 추가할 때 사용"}]}]}]},{"type":"paragraph","inlineContent":[{"text":"싱글턴 객체중 하나이므로 이벤트들의 발생 여부를 옵저버를 등록한 객체들에게 Notification을 post하는 방식으로 사용한다. Notification Name이라는 Key 값을 통해 보내고 받을 수 있다.","type":"text"}]},{"text":"장점","type":"heading","level":1,"anchor":"%EC%9E%A5%EC%A0%90"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"많은 줄의 코드가 필요없이 쉽게 구현이 가능하다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"다수의 객체들에게 동시에 이벤트의 발생을 알려줄 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Notification과 관련된 정보를 Any? 타입의 object, [AnyHashable : Any]? 타입의 userInfo로 전달할 수 있다."}]}]}]},{"level":1,"anchor":"%EB%8B%A8%EC%A0%90","text":"단점","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"key 값으로 Notification의 이름과 userInfo를 서로 맞추기 때문에 컴파일시 구독이 잘 되고 있는지, 올바르게 userInfo의 value를 받아오는지 체크가 불가능 하다"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"추적이 쉽지 않을 수도 있다"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Notification post 이후 정보를 받을 수 없다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"text":"Notification","anchor":"Notification","type":"heading","level":1},{"inlineContent":[{"text":"NotificationCenter를 통해 정보를 저장하기 위한 구조체다.","type":"text"},{"type":"text","text":" "},{"text":"옵저버들에게 전달되는 구조체로 정보가 담겨있고, 해당 알림을 등록한 옵저버에게만 전달된다. 구조체는 아래와 같이 구성되어 있다.","type":"text"}],"type":"paragraph"},{"syntax":"swift","code":["var name: Notification.Name","var object: Any?","userInfo: [AnyHashable : Any]?"],"type":"codeListing"},{"level":3,"anchor":"name","type":"heading","text":"name"},{"type":"paragraph","inlineContent":[{"type":"text","text":"전달하고자 하는 notification의 이름 (이걸 통해 알림을 식별한다)"}]},{"text":"object","level":3,"anchor":"object","type":"heading"},{"inlineContent":[{"type":"text","text":"발송자가 옵저버에게 보내려고 하는 객체. 주로 발송자 객체를 전달하는 데 쓰임"}],"type":"paragraph"},{"text":"userInfo","level":3,"anchor":"userInfo","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"notification과 관련된 값 또는 객체의 저장소. Extra data를 보내는데 사용 가능"}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/G0xAvnT.png"},{"type":"text","text":" "},{"text":"notification이 오면 observer pattern을 통해서 등록된 옵저버들에게 notification을 전달하기 위해 사용하는 클래스.","type":"text"},{"type":"text","text":" "},{"type":"text","text":"notification을 발송하면 NotificationCenter에서 메세지를 전달한 observer를 처리할 때까지 대기한다."},{"type":"text","text":" "},{"text":"즉, 흐름이 동기적으로 흘러간다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"otification Center를 통해서 앱의 한 파트에서 다른 파트로 데이터를 전달할 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Notification이 오면 등록된 observer list를 스캔한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Notification Center는 어플리케이션 어느 곳에서 어느 객체와도 상호작용을 할 수 있다."}]}]}]},{"type":"heading","level":2,"anchor":"%EC%83%81%ED%98%B8-%EC%9E%91%EC%9A%A9%EC%9D%84-%ED%95%98%EA%B8%B0-%EC%A0%84%EC%97%90-extension%EC%9C%BC%EB%A1%9C-NotificationName%EC%9D%84-%EC%B6%94%EA%B0%80%ED%95%B4%EC%A3%BC%EB%A9%B4-%ED%8E%B8%EB%A6%AC%ED%95%98%EB%8B%A4","text":"상호 작용을 하기 전에 extension으로 Notification.Name을 추가해주면 편리하다."},{"syntax":"swift","code":["\/\/ Notification Name 설정","extension Notification.Name {"," static let secret = Notification.Name(\"Shh\")","}"],"type":"codeListing"},{"inlineContent":[{"text":"NotificationCenter로 Post하기 (발송하기)","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"Post가 핵심이다. Name의 해당자들에게 일을 수행하라고 시킨다."}],"type":"paragraph"},{"code":["\/\/ 노티피케이션 발송","NotificationCenter.default.post(name: Notification.Name.secret, object: nil, userInfo: [NotificationKey.password: \"암호는 !@#$\"])"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"name","type":"text"},{"type":"text","text":" "},{"type":"text","text":"전달하고자 하는 notification의 이름 (이걸 통해 알림을 식별)"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"object"},{"text":" ","type":"text"},{"type":"text","text":"addObserver의 object 부분과 목적이 동일한데, 특정 sender의 notification만 받고 싶은 경우 작성 해주면 된다. filter 기능과 같다고 생각하면 될 것 같다. 없으면 nil"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"userInfo"},{"text":" ","type":"text"},{"type":"text","text":"notification과 관련된 값이다. extra data를 보내는데 사용한다."}]}]}],"type":"unorderedList"},{"type":"heading","level":1,"text":"Notification Center에 Observer 등록하기","anchor":"Notification-Center%EC%97%90-Observer-%EB%93%B1%EB%A1%9D%ED%95%98%EA%B8%B0"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"notification을 observe 해주기 전에 Notification Center에 ","type":"text"},{"code":"addObserver","type":"codeVoice"},{"type":"text","text":" 과정을 무조건 먼저 거쳐줘야 원하는 신호를 관찰 가능하니까 주의하도록 하자."}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"addObserver"},{"type":"text","text":"가 있으면 "},{"code":"removeObserver(_:name:object:)","type":"codeVoice"},{"type":"text","text":"도 있는데 방식은 같다."}],"type":"paragraph"}]}]},{"syntax":"swift","type":"codeListing","code":["\/\/ Notification Name 설정","NotificationCenter.default.addObserver(self, selector: #selector(answerToMaster(notification:)), name: Notification.Name.secret, object: nil)","","@objc func answerToMaster(notification: Notification) {"," \/\/ notification.userInfo 값을 받아온다."," guard let key = notification.userInfo?[NotificationKey.password] as? String else {"," return"," }"," print(\"\\(name): \\(key)\")","}"]},{"type":"heading","level":1,"anchor":"NotificationCenter%EB%8A%94-%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C","text":"NotificationCenter는 언제 사용해야할까?"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"앱 내에서 공식적인 연결이 없는 두 개 이상의 컴포넌트들이 상호작용이 필요할 때"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"상호작용이 반복적으로 그리고 지속적으로 이루어져야 할 때"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"일대다 또는 다대다 통신을 사용하는 경우"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"level":1,"text":"예제 코드 풀버전","type":"heading","anchor":"%EC%98%88%EC%A0%9C-%EC%BD%94%EB%93%9C-%ED%92%80%EB%B2%84%EC%A0%84"},{"syntax":"swift","type":"codeListing","code":["\/\/ Notification Name 설정","extension Notification.Name {"," static let secret = Notification.Name(\"Shh\")","}","","\/\/ Notification과 관련된 인스턴스","enum NotificationKey {"," case password","}","","class Master {"," func callPassword() {"," print(\"마스터: 벽면에 쓰여있는 암호를 읊어봐.\")"," \/\/ NotificationCenter로 Post하기 (발송하기)"," NotificationCenter.default.post(name: Notification.Name.secret, object: nil, userInfo: [NotificationKey.password: \"!@#$\"])"," }","}","","class Friend {"," let name: String"," "," init(name: String) {"," self.name = name"," \/\/ NotificationCenter에 Observer 등록하기"," NotificationCenter.default.addObserver(self, selector: #selector(answerToMaster(notification:)), name: Notification.Name.secret, object: nil)"," }"," @objc func answerToMaster(notification: Notification) {"," \/\/ notification.userInfo 값을 받아온다."," guard let object = notification.userInfo?[NotificationKey.password] as? String else {"," return"," }"," print(\"\\(name): 암호는 \\(object)\")"," }","}","","let master = Master()","","\/\/ 관찰자들 (observer)","let ariOwn = Friend(name: \"아리랑\")","let ariTwo = Friend(name: \"쓰리랑\")","let ariThree = Friend(name: \"아라리오\")","","\/\/ observer들에게 일을 수행하라고 시킨다","master.callPassword()","\/*","마스터: 벽면에 쓰여있는 암호를 읊어봐.","아리랑: 암호는 !@#$","쓰리랑: 암호는 !@#$","아라리오: 암호는 !@#$","\/*"]},{"type":"paragraph","inlineContent":[{"type":"text","text":"Delegate는 보통 Protocol을 정의하여 사용된다. Protocol이란 일종의 기능 명세서 같은 것으로 Delefate로 지정된 객체가 해야하는 메소드들의 원형을 적어놓는다. Delegate 역할을 하려는 객체는 이 Protocol을 따르며 원형만 있던 메소드들의 구현을 한다. 이렇게 세팅 후 이전 객체는 어떤 이벤트가 일어났을 시 Delegate로 지정한 객체에 알려줄 수 있다."}]},{"text":"장점","level":1,"type":"heading","anchor":"%EC%9E%A5%EC%A0%90"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"매우 엄격한 Syntax로 인해 프로토콜에 필요한 메서드들이 명확하게 명시된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"컴파일 시 경고나 에러가 떠서 프로토콜의 구현되지 않은 메소드들을 알려준다","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"로직의 흐름을 따라가기 쉽다","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"프로토콜 메소드로 알려주는 것 뿐만 아니라 정보를 받을 수 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"커뮤니케이션 과정을 유지하고 모니터링하는 제 3의 객체가 필요없다. (NotificationCenter같은 외부 객체)"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"프로토콜이 컨트롤러의 범위 안에서 정의된다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"anchor":"%EB%8B%A8%EC%A0%90","text":"단점","type":"heading","level":1},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"많은 줄의 코드가 필요하다","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"delegate 설정에 nil이 들어가지 않게 주의해야한다. 크래시를 일으킬 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"많은 객체들에게 이벤트를 알려주는 것이 어렵고 비효율적이다. (가능은 하다.)"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/cocoa_design_patterns\/using_key-value_observing_in_swift","type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/nskeyvalueobservingoptions","isActive":true,"type":"reference"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/notification","type":"reference","isActive":true}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/notificationcenter","isActive":true}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/medium.com\/@Alpaca_iOSStudy\/delegation-notification-%EA%B7%B8%EB%A6%AC%EA%B3%A0-kvo-82de909bd29","isActive":true}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"identifier":"https:\/\/zeddios.tistory.com\/1220","type":"reference"}]}]}]}]}]}],"kind":"content"}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"sections":[],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211020_KVC,_Notification,_NotificationCenter"},"metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","title":"211020 KVC, Notification, NotificationCenter"},"kind":"article","variants":[{"paths":["\/documentation\/arinote\/211020_kvc,_notification,_notificationcenter"],"traits":[{"interfaceLanguage":"swift"}]}],"references":{"https://developer.apple.com/documentation/foundation/nskeyvalueobservingoptions":{"title":"KVO Option","url":"https:\/\/developer.apple.com\/documentation\/foundation\/nskeyvalueobservingoptions","identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/nskeyvalueobservingoptions","type":"link","titleInlineContent":[{"text":"KVO Option","type":"text"}]},"https://zeddios.tistory.com/1220":{"title":"Blog - KVO","url":"https:\/\/zeddios.tistory.com\/1220","identifier":"https:\/\/zeddios.tistory.com\/1220","type":"link","titleInlineContent":[{"text":"Blog - KVO","type":"text"}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://developer.apple.com/documentation/swift/cocoa_design_patterns/using_key-value_observing_in_swift":{"title":"KVO","url":"https:\/\/developer.apple.com\/documentation\/swift\/cocoa_design_patterns\/using_key-value_observing_in_swift","identifier":"https:\/\/developer.apple.com\/documentation\/swift\/cocoa_design_patterns\/using_key-value_observing_in_swift","type":"link","titleInlineContent":[{"text":"KVO","type":"text"}]},"https://github.com/leeari95/TIL/blob/main/2021-10/211012%20%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%2C%20Properties%2C%20KeyPath%2C%20KVC%2C%20iOS%2C%20HIG.md":{"title":"KVC","url":"https:\/\/github.com\/leeari95\/TIL\/blob\/main\/2021-10\/211012%20%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%2C%20Properties%2C%20KeyPath%2C%20KVC%2C%20iOS%2C%20HIG.md","identifier":"https:\/\/github.com\/leeari95\/TIL\/blob\/main\/2021-10\/211012%20%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%2C%20Properties%2C%20KeyPath%2C%20KVC%2C%20iOS%2C%20HIG.md","type":"link","titleInlineContent":[{"text":"KVC","type":"text"}]},"https://developer.apple.com/documentation/foundation/notificationcenter":{"title":"NotificationCenter","url":"https:\/\/developer.apple.com\/documentation\/foundation\/notificationcenter","identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/notificationcenter","type":"link","titleInlineContent":[{"text":"NotificationCenter","type":"text"}]},"https://i.imgur.com/G0xAvnT.png":{"variants":[{"url":"https:\/\/i.imgur.com\/G0xAvnT.png","traits":["1x","light"]}],"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/G0xAvnT.png"},"https://developer.apple.com/documentation/foundation/notification":{"title":"Notification","url":"https:\/\/developer.apple.com\/documentation\/foundation\/notification","identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/notification","type":"link","titleInlineContent":[{"text":"Notification","type":"text"}]},"https://medium.com/@Alpaca_iOSStudy/delegation-notification-%EA%B7%B8%EB%A6%AC%EA%B3%A0-kvo-82de909bd29":{"title":"Blog - Delegation, Notification, 그리고 KVO","url":"https:\/\/medium.com\/@Alpaca_iOSStudy\/delegation-notification-%EA%B7%B8%EB%A6%AC%EA%B3%A0-kvo-82de909bd29","identifier":"https:\/\/medium.com\/@Alpaca_iOSStudy\/delegation-notification-%EA%B7%B8%EB%A6%AC%EA%B3%A0-kvo-82de909bd29","type":"link","titleInlineContent":[{"text":"Blog - Delegation, Notification, 그리고 KVO","type":"text"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211021_initalizer,_hig,_notification,_alert,_segue,_storyboard.json b/docs/data/documentation/arinote/211021_initalizer,_hig,_notification,_alert,_segue,_storyboard.json new file mode 100644 index 0000000..de8db91 --- /dev/null +++ b/docs/data/documentation/arinote/211021_initalizer,_hig,_notification,_alert,_segue,_storyboard.json @@ -0,0 +1 @@ +{"metadata":{"roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}],"title":"211021 initalizer, HIG, Notification, Alert, Segue, Storyboard"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211021_initalizer,_hig,_notification,_alert,_segue,_storyboard"]}],"kind":"article","primaryContentSections":[{"kind":"content","content":[{"type":"heading","text":"TIL (Today I Learned)","level":1,"anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"type":"text","text":"10월 21일 (목)"}]},{"level":2,"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"inlineContent":[{"type":"text","text":"오늘은 프로퍼티, 이니셜라이저, 노티피케이션 등 여러가지의 강의를 들었다. 이니셜라이저는 아직도 어려운 부분인데, Convenience initalizer의 설명을 들으면서 잊었었던 지식을 다시한번 기억해냈고, 이니셜라이저를 안쓰다보니까 어떻게 활용해야할지 몰랐는데 프로젝트에서 받았던 피드백이 기억나서 바로 고쳐먹어야겠다는 생각이 들었다. 그리고 노티피케이션도 "},{"type":"reference","isActive":true,"identifier":"https:\/\/github.com\/leeari95\/TIL\/blob\/main\/2021-10\/211020%20KVC%2C%20Notification%2C%20NotificationCenter.md"},{"type":"text","text":"때 너무 어려웠던 내용인데 강의를 들어도 아직 어렵다… 프로젝트에서는 어떤식으로 적용해야할지 감이 안잡힌다. 사용방법은 알겠는데 어디서 어떤식으로 적용을 시켜야 할지 모르겠다… 한방에 이해하기엔 어려운 내용이라 그래서 안심이 됬다. 무한 복습을 하는 수 밖에…"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"level":2,"text":"문제점 \/ 고민한 점","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Convenience initalizer는 클래스에서만 사용이 가능했던가? 기억이 가물가물…","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"그렇다면 구조체에서 다른 이니셜라이저를 생성해주려면 어떻게 해야할까?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Notification의 주요 프로퍼티중에서 object와 userInfo가 정확하게 무슨 역할을 하는 것인지?","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"프로젝트 STEP 2 내용을 봤는데 어떻게 해야하는건지 하나도 모르겠다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Alert을 공부하는 도중 버튼 위치 규칙같은게 있나 찾아보았다.","type":"text"}]}]}]},{"text":"해결방법","level":2,"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Convenience initalizer는 클래스에서만 사용이 가능하다. 구조체에서는 Convenience 키워드를 빼고 그냥 init으로 또다른 이니셜라이저를 생성해주면 된다. 편의 이니셜라이저와 같은 방식으로 만들 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Notification"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"name"}]},{"text":" 알림을 식별하는 태그","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"object"}]},{"type":"text","text":" 발송자가 옵저버에게 보내려고 하는 객체, 주로 발송자 객체를 전달하는데 쓰인다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"userInfo"}]},{"text":" 노티피케이션과 관련된 값 또는 객체의 저장소","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"예) 특정 행동으로 인해 작업이 시작되거나 완료되는 시점에 다른 인스턴스로 노티피케이션이 발생 시 필요한 데이터를 같이 넘겨줄 수 있다. 간단한 예로 네트워킹을 이용하는 애플리케이션이라면 네트워킹이 시작 및 완료되는 시점, 음악 및 동영상 재생 등에도 재생이 끝나는 시점에 관련 정보를 넘겨줄 수 있다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"Alert 만들기와 Storyboad Segue에 대해서 학습하고 예제를 만들어보았다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Alert을 만들때 2개 이상 액션버튼은 actionSheet으로 만들어줘야 하며 cancel 버튼은 좌측에 위치해야한다.","type":"text"}],"type":"paragraph"}]}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"level":2,"text":"공부내용 정리","type":"heading","anchor":"%EA%B3%B5%EB%B6%80%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC"},{"anchor":"%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-%EB%B9%8C%EB%8D%94%EC%97%90%EC%84%9C-%EC%A7%81%EC%A0%91-%EC%97%B0%EA%B2%B0%ED%95%98%EA%B8%B0","type":"heading","level":1,"text":"인터페이스 빌더에서 직접 연결하기"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"일단 2개의 View Controller를 준비한 후 ‘Go to B’ 버튼을 올려준다."},{"type":"text","text":" "},{"type":"text","text":" "}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그리고 ‘Go to B’ 버튼을 선택 후 Ctrl 키를 누른채로 B View Controller로 드래그를 하면 연결할 수 있는 Segue의 종류가 뜬다.","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/iOCdlT5.gif","type":"image"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Segue로 Show를 선택하면 화살표가 하나 생기는데, 이게 Segue가 연결되었다는 의미이고, Segue를 선택한 후 Inspector에서 여러 설정을 바꿀 수도 있다.","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/QQzdEg6.png"}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"결과 화면","type":"text"},{"type":"text","text":" "},{"type":"text","text":"이 예제에서는 Modal처럼 화면이 아래서 올라오는데, 옆으로 넘어가는 효과를 적용하려면 A ViewController를 Navigation Controller에 Embed 해주어야 한다."}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/edhPXRj.gif"}],"type":"paragraph"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"text":"직접 연결하는 방법은 ViewController 간의 상관관계를 직관적으로 알기는 좋지만 코드 내에서 ViewController 간의 관계에 따라 코드를 작성하기는 좀 어렵다. 그리고 하다보면 내가 Segue를 어디에 연결했는지 계속 확인해야하고, 처음에 연결할 때는 편리하긴 하지만 뒤로 갈수록 불편한 점이 많다.)","type":"text"}]},{"inlineContent":[{"text":"또한 Segue가 여러개 필요하다던지 ViewController 간의 데이터 전달이 필요하다던지 할 때는 performSegue나 present를 쓰는 것이 훨씬 편하다.","type":"text"}],"type":"paragraph"},{"type":"thematicBreak"},{"text":"Segue의 Identifier 설정 후 코드에서 performSegue 하기","type":"heading","anchor":"Segue%EC%9D%98-Identifier-%EC%84%A4%EC%A0%95-%ED%9B%84-%EC%BD%94%EB%93%9C%EC%97%90%EC%84%9C-performSegue-%ED%95%98%EA%B8%B0","level":1},{"inlineContent":[{"type":"text","text":"이번에는 Inspector에서 Segue의 Identifier를 설정한 후 코드에서 화면 전환이 필요한 때에 performSegue 메소드를 사용하여 Segue 객체를 생성해보자."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"우선 위의 예제처럼 스토리보드에서 Segue를 연결해준다. 근데 Button에서 Ctrl로 끌어가는 것이 아니라 A View Controller의 위에있는 버튼에서 끌고 간다.","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/KjEibf7.gif","type":"image"}],"type":"paragraph"},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"그리고 Segue 화살표를 누른다."}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/QpQ39Rd.png","type":"image"}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Inspector에 보면 Segue의 Identifier를 설정할 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/P45hMeT.png"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"그리고 ‘Go to B’ 버튼을 IBAction 메서드를 만들어 준 후 메소드 안에서 performSegue(withIdentifier: sender:) 를 구현해준다. 이렇게 하면 내가 원하는 Segue의 identifier를 넣어주기만 하면 원하는 View로의 전환이 가능하다.","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/WobhDWx.gif","type":"image"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/2dhh3c7.gif"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"aside","style":"note","name":"performSegue 메소드가 실행되기 전에 prepare(for segue","content":[{"inlineContent":[{"text":"Sender:) 메소드가 항상 먼저 실행되는데, ViewController 간에 데이터를 전달해야 할 때 이 prepare(for segue: sender:)를 이용한다.","type":"text"}],"type":"paragraph"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"heading","text":"StoryBoard ID 설정 후 코드에서 present 하기","anchor":"StoryBoard-ID-%EC%84%A4%EC%A0%95-%ED%9B%84-%EC%BD%94%EB%93%9C%EC%97%90%EC%84%9C-present-%ED%95%98%EA%B8%B0","level":1},{"type":"paragraph","inlineContent":[{"type":"text","text":"이번에는 Ctrl로 드래그앤드롭 하여 Segue를 연결해줄 필요 없이 각 ViewController의 StoryBoard ID를 지정해주고 코드에서 present(_: animated: completion:) 라는 메서드를 이용해보자."},{"text":" ","type":"text"},{"type":"text","text":" "}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"B View Controller를 선택 후 Inspector에서 Identity > Storyboard ID를 “ViewB”로 설정해준다."}]}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/wjFDUI5.png"}]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"IBAction 버튼 메서드 내부에서 instantiateViewController(identifier:) 메서드를 이용하여 UIViewController를 가져온다. 이때 옵셔널 타입이므로 옵셔널 바인딩해준다."}]}]}]},{"syntax":"swift","type":"codeListing","code":["@IBAction func goToB(_ sender: UIButton) {"," guard let viewB = self.storyboard?.instantiateViewController(identifier: \"ViewB\") else {"," return"," }"," present(viewB, animated: true, completion: nil)","}"]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그런 다음 위와 같이 present 메소드를 써서 ViewB를 보여달라고 말하면 된다."}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"알림창을 만들어보자."}]},{"anchor":"IBAction-%EB%A7%8C%EB%93%A4%EA%B8%B0","level":1,"type":"heading","text":"IBAction 만들기"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"버튼을 Ctrl을 누르면서 코드쪽으로 드래그하면 된다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/lSNTWk6.png"}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Name칸을 채워주고, Type을 UIButton으로 설정해준 뒤 Connect 버튼을 클릭해준다.","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Th6xCfz.png"}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"그럼 이러한 함수가 뙇! 생기는데 이 내부에다가 코드를 적어줄 것이다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/TXDwzpA.png","type":"image"}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"level":2,"text":"Alert 제목과 메세지내용 만들기.","type":"heading","anchor":"Alert-%EC%A0%9C%EB%AA%A9%EA%B3%BC-%EB%A9%94%EC%84%B8%EC%A7%80%EB%82%B4%EC%9A%A9-%EB%A7%8C%EB%93%A4%EA%B8%B0"},{"syntax":"swift","type":"codeListing","code":["let alret = UIAlertController(title: \"알림\", message: \"알림창 내용\", preferredStyle: .alert)"]},{"type":"paragraph","inlineContent":[{"text":"위와 같이 UIAlertController를 이용하여 파라미터를 채워준다. preferredStyle은 아래와 같이 두가지가 있다.","type":"text"}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Alert","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/SgDL69z.png"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"actionSheet","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/3MCmYER.png"}]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"anchor":"Alert%EC%97%90-%EB%93%A4%EC%96%B4%EA%B0%88-%EC%95%A1%EC%85%98%EB%B2%84%ED%8A%BC-%EB%A7%8C%EB%93%A4%EA%B8%B0","level":2,"type":"heading","text":"Alert에 들어갈 액션버튼 만들기"},{"type":"paragraph","inlineContent":[{"text":"위 사진과 같은 Yes, No 같은 버튼을 따로 구현해줘야 한다.","type":"text"}]},{"syntax":"swift","code":["let yes = UIAlertAction(title: \"Yes\", style: .default, handler: nil)","let no = UIAlertAction(title: \"No\", style: .destructive, handler: nil)"],"type":"codeListing"},{"level":3,"text":"style","anchor":"style","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"cancel과 default는 글씨 굵기 빼고 동일하다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"destructive는 빨간 글씨로 표시된다."},{"type":"text","text":" "},{"type":"text","text":" "}],"type":"paragraph"}]}],"type":"unorderedList"},{"text":"handler","anchor":"handler","level":3,"type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"버튼을 눌렀을 때 실행해야하는 행동을 추가하는 부분이다. 추가를 하겠다면","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/iWQ02Mr.png"},{"text":" ","type":"text"},{"type":"text","text":" "}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 파란색 부분에서 엔터를 치면"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/X1e0pjp.png","type":"image"},{"type":"text","text":" "},{"type":"text","text":" "}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이렇게 클로저가 생기는데 내부에 코드를 작성해주면 된다."},{"text":" ","type":"text"},{"type":"text","text":" "},{"text":" ","type":"text"},{"type":"text","text":"나는 아무것도 안할거기 때문에 nil을 넣어주었다."},{"type":"text","text":" "},{"text":" ","type":"text"}]}]}]},{"type":"heading","level":2,"anchor":"%EB%A7%8C%EB%93%A4%EC%96%B4%EC%A4%80-UIAlertController%EC%99%80-UIAlertAction%EC%9D%84-%EC%97%B0%EA%B2%B0%ED%95%B4%EC%A3%BC%EA%B8%B0","text":"만들어준 UIAlertController와 UIAlertAction을 연결해주기"},{"inlineContent":[{"text":"지금까지 alert의 제목과 메세지, 그리고 액션버튼을 만들어주었다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":"이것들을 합쳐주는 작업이 필요하다."}],"type":"paragraph"},{"code":["alret.addAction(no)","alret.addAction(yes)"],"syntax":"swift","type":"codeListing"},{"inlineContent":[{"text":"이렇게 말이다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":" "}],"type":"paragraph"},{"level":2,"type":"heading","text":"alert view를 화면에 뜨게 만들어주기","anchor":"alert-view%EB%A5%BC-%ED%99%94%EB%A9%B4%EC%97%90-%EB%9C%A8%EA%B2%8C-%EB%A7%8C%EB%93%A4%EC%96%B4%EC%A3%BC%EA%B8%B0"},{"code":["present(alret, animated: true, completion: nil)"],"type":"codeListing","syntax":"swift"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Present 메서드를 통해서 alert을 뜨게 만들어준다."}]},{"text":"animated","type":"heading","level":3,"anchor":"animated"},{"type":"paragraph","inlineContent":[{"text":"animated를 true하면 애니메이션 같이 부드럽게 뜨고, false로 하면 그냥 빡!!! 하고 나타난다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"잘 모르겠다면 true, false를 바꿔가면서 테스트 해보자."}]},{"type":"heading","level":3,"anchor":"completion","text":"completion"},{"type":"paragraph","inlineContent":[{"text":"이것도 위에서 봤던 핸들러랑 같은 의미의 파라미터다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/fpbegh7.png"}]},{"inlineContent":[{"type":"text","text":"ompletion을 위와 같은 방법으로 엔터를 친다면 위와 같이 클로저가 생기면서 코드를 적을 수 있다."},{"text":" ","type":"text"},{"type":"text","text":"해당 alret이 성공적으로 수행되고 나서 이 함수가 끝난 뒤 뭘 할거냐? 라고 지정해주는 부분이라고 보면 된다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"type":"heading","text":"요약","level":2,"anchor":"%EC%9A%94%EC%95%BD"},{"type":"orderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"만들고 싶은 alert을 만든다"},{"type":"text","text":" "},{"code":"let alret = UIAlertController(title: \"알림\", message: \"알림창 내용\", preferredStyle: .alert)","type":"codeVoice"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"액션 버튼을 만든다.","type":"text"},{"type":"text","text":" "},{"code":"let yes = UIAlertAction(title: \"Yes\", style: .default, handler: nil)","type":"codeVoice"},{"text":" ","type":"text"},{"type":"codeVoice","code":"let no = UIAlertAction(title: \"No\", style: .destructive, handler: nil)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"alert 컨트롤러와 내가 만든 액션버튼을 addAction 메서드를 이용해서 바인딩해준다.","type":"text"},{"type":"text","text":" "},{"type":"codeVoice","code":"alret.addAction(no)"},{"text":" ","type":"text"},{"code":"alret.addAction(yes)","type":"codeVoice"}]}]},{"content":[{"inlineContent":[{"text":"present 한다.","type":"text"},{"type":"text","text":" "},{"type":"codeVoice","code":"present(alret, animated: true, completion: nil)"},{"text":" ","type":"text"},{"type":"text","text":" "}],"type":"paragraph"}]}]},{"anchor":"%EC%A3%BC%EC%9D%98%ED%95%B4%EC%95%BC%ED%95%A0-%EC%A0%90","level":2,"type":"heading","text":"주의해야할 점"},{"type":"orderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"alert에 추가하는 순서대로 왼쪽부터 나타나게 된다."},{"type":"text","text":" "},{"type":"text","text":"alertAction을 만드는 순서는 상관없고 추가하는 순서가 중요하다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"UIAlertController 안에 UIAlertActionStyle이 cancel인 액션버튼이 두개이상 들어갈 수 없다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"액션버튼을 두가지 이상으로 사용하고 싶다면 .alert 대신 .actionSheet style을 사용한다.","type":"text"},{"text":" ","type":"text"},{"text":" ","type":"text"}]}]}]},{"type":"heading","text":"Full Code","level":2,"anchor":"Full-Code"},{"type":"codeListing","code":[" @IBAction func printAlret(_ sender: UIButton) {"," let alret = UIAlertController(title: \"알림\", message: \"알림창 내용\", preferredStyle: .alert)"," let yes = UIAlertAction(title: \"Yes\", style: .default, handler: nil)"," let no = UIAlertAction(title: \"No\", style: .destructive, handler: nil)"," "," alret.addAction(no)"," alret.addAction(yes)",""," present(alret, animated: true, completion: nil)"," }"],"syntax":"swift"},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","isActive":true,"identifier":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/user-interaction\/undo-and-redo\/"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/yagom.net\/courses\/ios-starter-uikit\/","type":"reference","isActive":true}]}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/Initialization.html","type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/zeddios.tistory.com\/111?category=682195"}]}]}]}]}],"type":"unorderedList"}]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211021_initalizer,_HIG,_Notification,_Alert,_Segue,_Storyboard","interfaceLanguage":"swift"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"sections":[],"schemaVersion":{"patch":0,"minor":3,"major":0},"references":{"https://i.imgur.com/wjFDUI5.png":{"identifier":"https:\/\/i.imgur.com\/wjFDUI5.png","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/wjFDUI5.png"}]},"https://docs.swift.org/swift-book/LanguageGuide/Initialization.html":{"identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/Initialization.html","url":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/Initialization.html","type":"link","titleInlineContent":[{"text":"initalizer","type":"text"}],"title":"initalizer"},"https://i.imgur.com/X1e0pjp.png":{"identifier":"https:\/\/i.imgur.com\/X1e0pjp.png","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/X1e0pjp.png"}]},"https://i.imgur.com/QQzdEg6.png":{"identifier":"https:\/\/i.imgur.com\/QQzdEg6.png","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/QQzdEg6.png"}]},"https://i.imgur.com/Th6xCfz.png":{"identifier":"https:\/\/i.imgur.com\/Th6xCfz.png","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/Th6xCfz.png"}]},"https://i.imgur.com/WobhDWx.gif":{"identifier":"https:\/\/i.imgur.com\/WobhDWx.gif","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/WobhDWx.gif"}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/edhPXRj.gif":{"identifier":"https:\/\/i.imgur.com\/edhPXRj.gif","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/edhPXRj.gif"}]},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/iWQ02Mr.png":{"identifier":"https:\/\/i.imgur.com\/iWQ02Mr.png","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/iWQ02Mr.png"}]},"https://i.imgur.com/2dhh3c7.gif":{"identifier":"https:\/\/i.imgur.com\/2dhh3c7.gif","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/2dhh3c7.gif"}]},"https://i.imgur.com/P45hMeT.png":{"identifier":"https:\/\/i.imgur.com\/P45hMeT.png","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/P45hMeT.png"}]},"https://zeddios.tistory.com/111?category=682195":{"identifier":"https:\/\/zeddios.tistory.com\/111?category=682195","url":"https:\/\/zeddios.tistory.com\/111?category=682195","type":"link","titleInlineContent":[{"text":"Blog - Alert만들기","type":"text"}],"title":"Blog - Alert만들기"},"https://i.imgur.com/iOCdlT5.gif":{"identifier":"https:\/\/i.imgur.com\/iOCdlT5.gif","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/iOCdlT5.gif"}]},"https://i.imgur.com/lSNTWk6.png":{"identifier":"https:\/\/i.imgur.com\/lSNTWk6.png","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/lSNTWk6.png"}]},"https://i.imgur.com/TXDwzpA.png":{"identifier":"https:\/\/i.imgur.com\/TXDwzpA.png","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/TXDwzpA.png"}]},"https://github.com/leeari95/TIL/blob/main/2021-10/211020%20KVC%2C%20Notification%2C%20NotificationCenter.md":{"identifier":"https:\/\/github.com\/leeari95\/TIL\/blob\/main\/2021-10\/211020%20KVC%2C%20Notification%2C%20NotificationCenter.md","url":"https:\/\/github.com\/leeari95\/TIL\/blob\/main\/2021-10\/211020%20KVC%2C%20Notification%2C%20NotificationCenter.md","type":"link","titleInlineContent":[{"type":"strong","inlineContent":[{"text":"예습","type":"text"}]}],"title":"예습"},"https://yagom.net/courses/ios-starter-uikit/":{"identifier":"https:\/\/yagom.net\/courses\/ios-starter-uikit\/","url":"https:\/\/yagom.net\/courses\/ios-starter-uikit\/","type":"link","titleInlineContent":[{"text":"야곰 iOS 왕초보 강의","type":"text"}],"title":"야곰 iOS 왕초보 강의"},"https://i.imgur.com/SgDL69z.png":{"identifier":"https:\/\/i.imgur.com\/SgDL69z.png","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/SgDL69z.png"}]},"https://developer.apple.com/design/human-interface-guidelines/ios/user-interaction/undo-and-redo/":{"identifier":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/user-interaction\/undo-and-redo\/","url":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/user-interaction\/undo-and-redo\/","type":"link","titleInlineContent":[{"type":"text","text":"HIG-Undo and Redo"}],"title":"HIG-Undo and Redo"},"https://i.imgur.com/KjEibf7.gif":{"identifier":"https:\/\/i.imgur.com\/KjEibf7.gif","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/KjEibf7.gif"}]},"https://i.imgur.com/fpbegh7.png":{"identifier":"https:\/\/i.imgur.com\/fpbegh7.png","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/fpbegh7.png"}]},"https://i.imgur.com/QpQ39Rd.png":{"identifier":"https:\/\/i.imgur.com\/QpQ39Rd.png","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/QpQ39Rd.png"}]},"https://i.imgur.com/3MCmYER.png":{"identifier":"https:\/\/i.imgur.com\/3MCmYER.png","alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/3MCmYER.png"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211022_viewcontroller,_navigation_controller,_life_cycle.json b/docs/data/documentation/arinote/211022_viewcontroller,_navigation_controller,_life_cycle.json new file mode 100644 index 0000000..7b6e097 --- /dev/null +++ b/docs/data/documentation/arinote/211022_viewcontroller,_navigation_controller,_life_cycle.json @@ -0,0 +1 @@ +{"metadata":{"roleHeading":"Article","role":"article","title":"211022 ViewController, Navigation Controller, Life cycle","modules":[{"name":"Ari's Notes"}]},"sections":[],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211022_viewcontroller,_navigation_controller,_life_cycle"]}],"schemaVersion":{"minor":3,"major":0,"patch":0},"primaryContentSections":[{"content":[{"text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned","type":"heading","level":1},{"type":"paragraph","inlineContent":[{"type":"text","text":"10월 22일 (금)"}]},{"text":"학습 내용","level":2,"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"inlineContent":[{"type":"text","text":"오늘은 프로젝트 STEP2를 진행하려면 공부해야할 것들을 학습했다. 직접 예제를 만들어보면서 공부하니까 재밌고 이해하는데 있어서 도움이 많이되었다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"level":2,"type":"heading","text":"문제점 \/ 고민한 점","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Embed in 후 ViewController에 클래스를 지정해주고, 코드로 title을 바꿔주었는데, 적용이 제대로 안되었다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"프로젝트 내부 코드중 Recipe를 switch문으로 return하는 연산프로퍼티가 있는데, 다른 팀에서 하드코딩이라는 피드백을 받았다고 한다. 어떻게 하면 개선할 수 있을까…"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ViewController의 Life Cycle이 어렵진 않은 개념같은데 중요한 이유가 뭘까?"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","text":"해결방법","type":"heading","level":2},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"클래스를 자꾸 지웠다가 다시 만들어줘서 꼬인 것 같았다. 클래스 파일을 지우고 다시 새로 만들어 주었더니 해결…","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"여러가지 방법들을 생각하고 고민해보았지만 Recipe를 유연하게 만들 수 있는 좋은 방법을 찾지 못했다. 다른 리뷰어의 의견도 듣고싶어서 질문을 열심히 정리해서 DM을 보내보았다. 답장을 손꼽아 기다리며…","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"ViewController Life Cycle이 왜 중요할까?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"프로그래밍은 코드가 순차적으로 실행이 되는 알고리즘의 형태로 동작한다. 그렇기 때문에 한 화면이 동작하거나 열리고 닫히는 순간에도 철저하게 미묘한 차이의 순서대로 동작한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"화면이 열리고 닫히는 시점에 맞춰 음악이 재생되거나, 어떤 애니메이션이 동작할 때, 혹은 어떤 기능을 수행하게 될 때 Life Cycle에 따라 미묘한 차이가 발생한다. 이러한 오차에 대해 정확하게 반응하기 위해서는 뷰의 생명주기를 잘 이해할 필요가 있다.","type":"text"}],"type":"paragraph"}]}]}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"그렇다면 홈화면을 갔다가 오는 경우, 앱이 완전히 종료되는 경우에는 어떻게 동작하는 걸까?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"이 부분에 대해서는 Application의 Life Cycle을 공부해야할 것 같다.","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"type":"heading","level":2,"anchor":"%EA%B3%B5%EB%B6%80%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC","text":"공부내용 정리"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"identifier":"https:\/\/leeari95.tistory.com\/55","type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"identifier":"https:\/\/leeari95.tistory.com\/56","type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/leeari95.tistory.com\/57","isActive":true}]}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/view_controllers","type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uinavigationcontroller","type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/app_and_environment\/managing_your_app_s_life_cycle"}]}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211022_ViewController,_Navigation_Controller,_Life_cycle","interfaceLanguage":"swift"},"references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://leeari95.tistory.com/55":{"titleInlineContent":[{"type":"text","text":"[iOS] ViewController와 종류"}],"title":"[iOS] ViewController와 종류","url":"https:\/\/leeari95.tistory.com\/55","type":"link","identifier":"https:\/\/leeari95.tistory.com\/55"},"https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle":{"titleInlineContent":[{"type":"text","text":"App Life Cycle"}],"title":"App Life Cycle","url":"https:\/\/developer.apple.com\/documentation\/uikit\/app_and_environment\/managing_your_app_s_life_cycle","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/app_and_environment\/managing_your_app_s_life_cycle"},"https://developer.apple.com/documentation/uikit/view_controllers":{"titleInlineContent":[{"type":"text","text":"ViewControllers"}],"title":"ViewControllers","url":"https:\/\/developer.apple.com\/documentation\/uikit\/view_controllers","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/view_controllers"},"https://leeari95.tistory.com/57":{"titleInlineContent":[{"type":"text","text":"[iOS] ViewController의 Life cycle"}],"title":"[iOS] ViewController의 Life cycle","url":"https:\/\/leeari95.tistory.com\/57","type":"link","identifier":"https:\/\/leeari95.tistory.com\/57"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://developer.apple.com/documentation/uikit/uinavigationcontroller":{"titleInlineContent":[{"type":"text","text":"UINavigationController"}],"title":"UINavigationController","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uinavigationcontroller","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uinavigationcontroller"},"https://leeari95.tistory.com/56":{"titleInlineContent":[{"type":"text","text":"[iOS] Navigation Controller 맛보기"}],"title":"[iOS] Navigation Controller 맛보기","url":"https:\/\/leeari95.tistory.com\/56","type":"link","identifier":"https:\/\/leeari95.tistory.com\/56"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211023_initialization,_mvc,_notification_--.json b/docs/data/documentation/arinote/211023_initialization,_mvc,_notification_--.json new file mode 100644 index 0000000..1e50414 --- /dev/null +++ b/docs/data/documentation/arinote/211023_initialization,_mvc,_notification_--.json @@ -0,0 +1 @@ +{"schemaVersion":{"major":0,"patch":0,"minor":3},"metadata":{"role":"article","title":"211023 Initialization, MVC, Notification 복습","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article"},"primaryContentSections":[{"content":[{"anchor":"TIL-Today-I-Learned","type":"heading","level":1,"text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"type":"text","text":"10월 23일 (토)"}]},{"text":"학습 내용","level":2,"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"paragraph","inlineContent":[{"text":"오늘은 불타는 토요스터디를 통해서 이니셜라이저와 MVC 패턴, Notification에 대해서 복습하였다.","type":"text"}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","level":2,"text":"문제점 \/ 고민한 점","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"구조체에서는 Memberwise initalizer로 이니셜라이저를 따로 생성하지 않아도 되는데 클래스는 안되네?","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"초기화를 할때 구지 이니셜라이저를 쓰지 않고 기본값을 쓰는 경우는 언제일까?"}]}]},{"content":[{"inlineContent":[{"text":"View와 Controller는 분리하기가 어려운 구조인 것 같은데, 왜그럴까?","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"ViewController는 너무 많은 역할을 하는 것 같다…","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"MVC 패턴에서 Notification을 사용할 때 Observer는 어디에, post는 어디에 두면 좋을까?","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Notification으로 신호를 주고받을 때 데이터는 어떻게 전달할까?"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"text":"해결방법","level":2,"type":"heading","anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"클래스는 프로퍼티에 기본값이 없다면 이니셜라이저 구현이 필수적이다. 구조체는 기본값이 없고 이니셜라이저가 없어도 Memberwise initalizer라는 것으로 자동적으로 init이 구현된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이니셜라이저 델리게이트는 구조체는 그냥 init을 새로 구현하면되고 클래스는 convenience init을 만들어서 구현할 수 있다.","type":"text"}]}]}]},{"syntax":"swift","type":"codeListing","code":["\/\/ Memberwise initalizer","struct Human {"," let name: String"," var age: Int","}","","\/\/ Initializer Delegation, initalizer Extension","extension Human {"," init() {"," self.init(name: \"\", age: 0)"," }","}","","","\/\/ class 내부에는 반드시 한 개 이상의 Designated initalizer가 있어야 한다.","class Person {"," let name: String"," "," init(name: String) {"," self.name = name"," }"," "," convenience init() {"," self.init(name: \"\") \/\/ Designated initalizer 호출"," }","}"]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/Initialization.html#ID204","isActive":true},{"text":"에 ","type":"text"},{"code":"Default Property Values","type":"codeVoice"},{"text":"부분을 확인해보면 ‘항상 동일한 초기값’을 가지는 경우에 기본값을 제공하라고 나와있다. 예를 들어 아이폰의 이름이 항상 “OO의 IPhone” 인것처럼 초기값 제공이 필요한 경우에만 프로퍼티에 기본값을 제공하고 나머지 경우에는 코드의 유연성을 위해서 이니셜라이저를 쓰는 것이 좋을 것 같다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"애초에 ViewController가 상속받는 클래스 이름 자체가 UIViewController이다. View를 가지고 있는 Controller라서 분리자체가 어려운 것 같다. 또한 Model의 인스턴스까지 생성해줘야 하고, View의 Life Cycle까지 관리하기 때문에 분리하기 어렵고, 분리가 안되니까 재사용도, 테스트도 불가능한 것이라고 보여진다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Notification을 사용하여 옵저버등록과 발송을 어디서 어디로 해야하는지 헷갈렸었는데, 오늘 ","type":"text"},{"type":"codeVoice","code":"Wody"},{"type":"text","text":"가 나의 궁금증을 해소시켜주었다. 옵저버는 말그대로 신호를 받는, 즉 관찰자이기 때문에 신호가 올때까지 기다렸다가 신호가 오면 일을 해야 한다. 따라서 Model이 될 수 있을 것같고, Post는 신호를 보내서 일을 시키는 것이기 때문에 Controller가 될 수 있을 것 같다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"NotificationCenter에 Post에 Object를 통해 데이터를 전달해줄 수 있다!","type":"text"}],"type":"paragraph"}]}]},{"level":2,"type":"heading","text":"오늘의 깨달음","anchor":"%EC%98%A4%EB%8A%98%EC%9D%98-%EA%B9%A8%EB%8B%AC%EC%9D%8C"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"허황"},{"text":"이 ‘그럼 인스턴스를 Controller가 아니라 Model 파일 외부 전역변수로 생성해주면 되지않나?’ 라는 질문을 해서 ‘그럼 인스턴스가 전역으로 떠돌아 다니게 되니까 메모리 낭비가 될 것 같다’고 ","type":"text"},{"code":"Wody","type":"codeVoice"},{"type":"text","text":"가 답변해주었다. 다른 뷰로 가게되도 인스턴스가 살아있다. 즉, 불필요한 순간에 메모리를 쓰고 있다는 것이다. 이걸 듣고 설계할 때 나도 불필요하게 쓰이는 데이터가 없는지도 고려해야겠다는 생각이 들었다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"데이터는 꼭 필요한 곳에서만 생성하고 죽게 설계한다.","type":"text"}]}]}]}]}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/view_controllers","isActive":true,"type":"reference"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uinavigationcontroller","isActive":true,"type":"reference"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/app_and_environment\/managing_your_app_s_life_cycle","type":"reference","isActive":true}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211023_Initialization,_MVC,_Notification_--"},"variants":[{"paths":["\/documentation\/arinote\/211023_initialization,_mvc,_notification_--"],"traits":[{"interfaceLanguage":"swift"}]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"sections":[],"kind":"article","references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://docs.swift.org/swift-book/LanguageGuide/Initialization.html#ID204":{"type":"link","title":"공식문서","titleInlineContent":[{"text":"공식문서","type":"text"}],"url":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/Initialization.html#ID204","identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/Initialization.html#ID204"},"https://developer.apple.com/documentation/uikit/view_controllers":{"type":"link","title":"ViewControllers","titleInlineContent":[{"text":"ViewControllers","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/view_controllers","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/view_controllers"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://developer.apple.com/documentation/uikit/app_and_environment/managing_your_app_s_life_cycle":{"type":"link","title":"App Life Cycle","titleInlineContent":[{"text":"App Life Cycle","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/app_and_environment\/managing_your_app_s_life_cycle","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/app_and_environment\/managing_your_app_s_life_cycle"},"https://developer.apple.com/documentation/uikit/uinavigationcontroller":{"type":"link","title":"UINavigationController","titleInlineContent":[{"text":"UINavigationController","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uinavigationcontroller","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uinavigationcontroller"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211025_uinavigationbar,_localizederror,_self,_self,_protocol,_extension,_typecasting,_optional,_dismiss.json b/docs/data/documentation/arinote/211025_uinavigationbar,_localizederror,_self,_self,_protocol,_extension,_typecasting,_optional,_dismiss.json new file mode 100644 index 0000000..41c4dbe --- /dev/null +++ b/docs/data/documentation/arinote/211025_uinavigationbar,_localizederror,_self,_self,_protocol,_extension,_typecasting,_optional,_dismiss.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","type":"heading","level":1,"text":"TIL (Today I Learned)"},{"inlineContent":[{"type":"text","text":"10월 25일 (월)"}],"type":"paragraph"},{"text":"학습 내용","level":2,"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"paragraph","inlineContent":[{"text":"오늘은 활동학습 시간에 클래스와 구조체, 참조와 값, 그리고 싱글턴에 대해서 학습해보았다. 예전에 공부한 적이 있었지만 참조타입과 값타입은 역시 접하는 시간마다 매번 어려운 순간같다고 느껴지는 시간이였다. 이후 제이티와 함께 STEP 2 프로젝트를 구현하기 시작하였다.","type":"text"}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"level":2,"text":"문제점 \/ 고민한 점","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"STEP 2 요구사항을 구현하기전에 설계를 어떤식으로 하면 좋을까?","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Navigation View Controller가 두개인데.. 왜 두개지? 하나면 되지 않나?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"LocalizedError 프로토콜은 쓰라고 만들어 둔걸까?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"프로토콜을 채택후 Optional인 프로퍼티를 non-optional로 구현하면 안되는 건가?","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"얼럿을 예, 아니오로 표시하라고 되어있는데.. HIG 지침에는 하지말라고 되어있다. 이럴 경우 요구사항을 지켜야하는걸까?","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"self와 Self 차이점을 모르겠다."}]}]},{"content":[{"inlineContent":[{"text":"과일의 재고가 변동이 될때마다 화면에 반영이 되었으면 좋겠는데…","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로토콜을 채택한 타입을 사용할 때, 프로토콜에서 정의한 프로퍼티와 이름이 동일한 프로퍼티를 구현했다. 근데 왜 타입에서 구현한 프로퍼티를 안가져오는거지?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"dismiss가 뭐여…","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"level":2,"type":"heading","text":"해결방법","anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"요구사항을 하나씩 재현해나가며 코드를 어떤식으로 짤지 의논하였다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Navigation Bar를 이용하기 위한 설계였다. Navigation View Controller가 아니라면 Navigation Bar Button을 이용할 수가 없어서 버튼을 직접 구현해줘야 했다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"구글링을 해보아도 해당 프로토콜을 쓰는 사람을 찾기에는 어려웠다. 추측이지만 EncodingError라는 타입을 쓸 때 사용되는 것 같다."}]}]},{"content":[{"inlineContent":[{"text":"이름은 같고 타입이 다르면 그건 다른 프로퍼티인 것이다. 그러나 파라미터로 어떤 타입을 명시해주냐에 따라 그 타입에 맞는 프로퍼티가 적절하게 들어가는 것 같다는 추측을 해보았다. 질문-답변방에도 올려보았지만 이해가 되지 않았다…","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"야곰의 답변"}]},{"type":"text","text":" String과 String?은 "},{"code":"서로 다른 타입","type":"codeVoice"},{"type":"text","text":"이다. 파라미터 타입이 "},{"type":"codeVoice","code":"String?"},{"type":"text","text":"이라면, 그리고 파라미터를 전달할 후보가 "},{"type":"codeVoice","code":"String, String?"},{"text":" 두 가지가 있다면 컴파일러는 당연히 ","type":"text"},{"code":"String?","type":"codeVoice"},{"text":"을 전달해주려고 할 것이다. 다만 최후의 후보가 String 뿐이라서 String? 자리에 ","type":"text"},{"type":"codeVoice","code":"String"},{"text":"이 전달되는 경우 ","type":"text"},{"type":"strong","inlineContent":[{"text":"String","type":"text"}]},{"type":"text","text":"을 "},{"type":"codeVoice","code":"String?으로 포장"},{"type":"text","text":"해줄 수는 있겠다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"self"},{"type":"text","text":" 자기 자신을 나타내는 프로퍼티다."}]}]},{"content":[{"inlineContent":[{"code":"Self","type":"codeVoice"},{"text":" 자기 자신의 동적 타입을 나타낸다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Notification을 활용하여 재고수정 메서드가 호출될 때마다 post, 즉 알림을 발송하게 구현하였다. 그리고 ViewController에다가 옵저버를 추가하는 작업을 해주었다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"모달의 상태를 해제시켜주는, 모달로 제공한 View를 닫아주는 역할의 UIViewController의 인스턴스 메서드이다."},{"type":"text","text":" "},{"text":" ","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"안녕하세요. 늦은밤 실례하겠습니다ㅎㅎ…"},{"type":"text","text":" "},{"text":"프로토콜을 사용해보다가 이상한 걸 발견해서 질문 올려봅니다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"제가 원하는건 해결방법이 아니라 이유가 궁금해서요. 왜그런걸까요?"},{"type":"text","text":" "},{"text":"이 이슈를 재현하는 방법은 아래와 같습니다.","type":"text"}]},{"anchor":"%EC%83%98%ED%94%8C-%EC%BD%94%EB%93%9C","text":"샘플 코드","type":"heading","level":1},{"type":"codeListing","syntax":"swift","code":["\/\/ 프로토콜 정의","protocol Camper {"," var property: String? { get } \/\/ 옵셔널","}","\/\/ 프로토콜 기본 구현","extension Camper {"," public var property: String? { \/\/ 옵셔널"," return nil"," }","}","\/\/ 프로토콜 채택","struct Person: Camper {"," var property: String { \/\/ 옵셔널로 구현하지 않았음"," return \"Hello\""," }","}","","\/\/ 테스트 함수","func test(message: String?) {"," print(message) \/\/ 내가 기대한 결과 -> Opional(\"hello\")","}","","let ari = Person() \/\/ 인스턴스 생성"]},{"level":1,"text":"제가 기대한 결과물","type":"heading","anchor":"%EC%A0%9C%EA%B0%80-%EA%B8%B0%EB%8C%80%ED%95%9C-%EA%B2%B0%EA%B3%BC%EB%AC%BC"},{"type":"paragraph","inlineContent":[{"type":"text","text":"test(message: ari.property) \/\/ Optional(“hello”)"}]},{"type":"heading","text":"실제 결과물","anchor":"%EC%8B%A4%EC%A0%9C-%EA%B2%B0%EA%B3%BC%EB%AC%BC","level":1},{"type":"paragraph","inlineContent":[{"type":"text","text":"test(message: ari.property) \/\/ nil"}]},{"text":"코드 설명","anchor":"%EC%BD%94%EB%93%9C-%EC%84%A4%EB%AA%85","type":"heading","level":1},{"inlineContent":[{"text":"이해를 위해서 내용을 정리해보았습니다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이름이 같지만 타입은 다른 프로퍼티를 extension을 이용하여 사용하는 경우이다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Optinal과 Non-Optional"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"원래 이름은 같고 타입만 다른 프로퍼티는 만들수가 없는데, extension을 이용하면 가능해진다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Protocol에서 정의, extension으로 기본 구현을 하고, 그 프로토콜을 채택한 타입(Person)을 만든다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 extension에서 프로토콜이 정의한 프로퍼티를 구현한 프로퍼티를 재구현하는 것이 아니고,"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"타입은 다르지만 이름이 같은 프로퍼티를 만든다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로토콜이 정의한 프로퍼티는 Optinal"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"타입에서 정의한 프로퍼티는 Non-Optional이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이름은 동일하다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"함수에 파라미터로 프로토콜이 정의한 프로퍼티가 아니라 타입에서 정의한 프로퍼티를 전달한다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"일반적으로 기대하는 결과는 당연히 타입에서 구현한 프로퍼티의 값이 나올 거라는 것이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"그렇지만 타입을 정의하기 이전에 extension으로 구현한 값이 전달된다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"그것도 함수의 파라미터로 전달할 때에만…","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로퍼티를 타입을 명시하지않고 변수에 담아준다거나 print문을 사용할 땐 제대로 타입의 프로퍼티를 인식한다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"왜 내가 전달한 것은 String인데 왜 함수의 파라미터로 전달할 때에만 String?을 가져오는 것일까?"}],"type":"paragraph"}]}]},{"anchor":"%EB%82%98%EC%9D%98-%EC%B6%94%EC%B8%A1","text":"나의 추측","type":"heading","level":1},{"type":"paragraph","inlineContent":[{"text":"컴파일 시점에 이름이 동일한 프로퍼티가 2개가 있으니 타입이 같은 extension의 프로퍼티를 추론하여 가져온다 라는… 생각이 드네요.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"아래처럼 아예 타입을 지정해서 변수에 담아주면 컴파일이 알아서 들어갈 값을 정해서 넣어주는 현상을 볼 수 있습니다."}]},{"code":["let testtest: String? = ari.property \/\/ nil","let test: String = ari.property \/\/ “Hello”"],"type":"codeListing","syntax":"swift"},{"inlineContent":[{"text":"왜 이런 현상이 발생하는지 근거를 찾아보려고 해보았으나 찾지못했어요. 찾지못해서 추측만 해보는 상황이네요…","type":"text"},{"type":"text","text":" "},{"type":"text","text":"다른분들의 의견도 궁금해서 올려봅니다."}],"type":"paragraph"},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/stackoverflow.com\/questions\/62265690\/swift-protocol-conformance-when-same-property-name-is-optional","type":"reference","isActive":true}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/www.hackingwithswift.com\/example-code\/language\/self-vs-self-whats-the-difference","type":"reference","isActive":true}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/localizederror","isActive":true,"type":"reference"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uinavigationbar","isActive":true}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621505-dismiss","isActive":true}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211025_UINavigationBar,_LocalizedError,_self,_Self,_protocol,_extension,_TypeCasting,_Optional,_dismiss","interfaceLanguage":"swift"},"sections":[],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211025_uinavigationbar,_localizederror,_self,_self,_protocol,_extension,_typecasting,_optional,_dismiss"]}],"metadata":{"role":"article","title":"211025 UINavigationBar, LocalizedError, self, Self, protocol, extension, TypeCasting, Optional, dismiss","roleHeading":"Article","modules":[{"name":"Ari's Notes"}]},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"schemaVersion":{"patch":0,"major":0,"minor":3},"references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://developer.apple.com/documentation/uikit/uinavigationbar":{"type":"link","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uinavigationbar","title":"UINavigationBar","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uinavigationbar","titleInlineContent":[{"text":"UINavigationBar","type":"text"}]},"https://developer.apple.com/documentation/foundation/localizederror":{"type":"link","url":"https:\/\/developer.apple.com\/documentation\/foundation\/localizederror","title":"LocalizedError","identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/localizederror","titleInlineContent":[{"text":"LocalizedError","type":"text"}]},"https://www.hackingwithswift.com/example-code/language/self-vs-self-whats-the-difference":{"type":"link","url":"https:\/\/www.hackingwithswift.com\/example-code\/language\/self-vs-self-whats-the-difference","title":"self vs Self","identifier":"https:\/\/www.hackingwithswift.com\/example-code\/language\/self-vs-self-whats-the-difference","titleInlineContent":[{"text":"self vs Self","type":"text"}]},"https://stackoverflow.com/questions/62265690/swift-protocol-conformance-when-same-property-name-is-optional":{"type":"link","url":"https:\/\/stackoverflow.com\/questions\/62265690\/swift-protocol-conformance-when-same-property-name-is-optional","title":"동일한 프로퍼티 이름이 주어졌을 경우","identifier":"https:\/\/stackoverflow.com\/questions\/62265690\/swift-protocol-conformance-when-same-property-name-is-optional","titleInlineContent":[{"text":"동일한 프로퍼티 이름이 주어졌을 경우","type":"text"}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://developer.apple.com/documentation/uikit/uiviewcontroller/1621505-dismiss":{"type":"link","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621505-dismiss","title":"dismiss","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621505-dismiss","titleInlineContent":[{"text":"dismiss","type":"text"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211026_device_orientation,_navigationviewcontroller,_madal,_gesture,_naming.json b/docs/data/documentation/arinote/211026_device_orientation,_navigationviewcontroller,_madal,_gesture,_naming.json new file mode 100644 index 0000000..de27894 --- /dev/null +++ b/docs/data/documentation/arinote/211026_device_orientation,_navigationviewcontroller,_madal,_gesture,_naming.json @@ -0,0 +1 @@ +{"kind":"article","schemaVersion":{"major":0,"patch":0,"minor":3},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211026_device_orientation,_navigationviewcontroller,_madal,_gesture,_naming"]}],"sections":[],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211026_Device_Orientation,_NavigationViewController,_madal,_Gesture,_Naming","interfaceLanguage":"swift"},"metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","title":"211026 Device Orientation, NavigationViewController, madal, Gesture, Naming"},"primaryContentSections":[{"kind":"content","content":[{"level":1,"anchor":"TIL-Today-I-Learned","type":"heading","text":"TIL (Today I Learned)"},{"inlineContent":[{"text":"10월 26일 (화)","type":"text"}],"type":"paragraph"},{"text":"학습 내용","type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"inlineContent":[{"text":"오늘은 STEP 2의 코드를 개선하고, 이후 PR 작성과 README 작성을 진행하였다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"type":"heading","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","text":"문제점 \/ 고민한 점","level":2},{"items":[{"content":[{"inlineContent":[{"text":"Application의 가로모드와 세로모드를 제어할 수 있는 방법이 뭐지?","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"NavigationViewController가 두개인 이유?","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"madal의 경우 세로모드에서는 위에서 아래로 내리는 제스처를 사용할 수 있는데 가로모드에서 하려면 어떻게 해야하는거지?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIButton과 UILabel에 대해서 알아보자"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"UI 관련 타입들을 네이밍할 때 끝에 Label이나 Button을 넣어주는 것이 널리 쓰이는 방법 같은데 적절한걸까?"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"type":"heading","level":2,"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","text":"해결방법"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Device Orientation"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이 부분을 통해서 세로모드, 가로모드를 제어할 수 있다."},{"type":"text","text":" "},{"type":"image","identifier":"https:\/\/i.imgur.com\/g4JYmeB.png"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Navigation Bar의 아이템들을 활용해야하기 때문에 2개가 있다고 생각했다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"아이폰의 기본앱 "},{"code":"메일","type":"codeVoice"},{"text":"을 바탕으로 실험해보았으나 가로모드에서는 위에서 쓸어내려서 modal을 끌 수가 없었다. 세로모드에서만 가능한 걸로…","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이론적으로 정리해서 적어보았는데, 내일 Stepper도 함께 배워야할 것 같다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이 부분은 적절한건지 감이 안와서 리뷰어에게 질문해보기로 하였다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"items":[{"content":[{"inlineContent":[{"identifier":"https:\/\/jongwonwoo.medium.com\/ios-%EB%94%94%EB%B0%94%EC%9D%B4%EC%8A%A4-%ED%9A%8C%EC%A0%84-%EC%B2%98%EB%A6%AC%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A0%95%EB%A6%AC-340f37204a27","type":"reference","isActive":true}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uideviceorientation"}]}]},{"content":[{"inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uibutton"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uilabel","isActive":true,"type":"reference"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}],"references":{"https://developer.apple.com/documentation/uikit/uibutton":{"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uibutton","titleInlineContent":[{"type":"text","text":"UIButton"}],"type":"link","title":"UIButton","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uibutton"},"https://developer.apple.com/documentation/uikit/uideviceorientation":{"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uideviceorientation","titleInlineContent":[{"type":"text","text":"UIDeviceorientation"}],"type":"link","title":"UIDeviceorientation","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uideviceorientation"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/g4JYmeB.png":{"alt":null,"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/g4JYmeB.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/g4JYmeB.png"},"https://jongwonwoo.medium.com/ios-%EB%94%94%EB%B0%94%EC%9D%B4%EC%8A%A4-%ED%9A%8C%EC%A0%84-%EC%B2%98%EB%A6%AC%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A0%95%EB%A6%AC-340f37204a27":{"url":"https:\/\/jongwonwoo.medium.com\/ios-%EB%94%94%EB%B0%94%EC%9D%B4%EC%8A%A4-%ED%9A%8C%EC%A0%84-%EC%B2%98%EB%A6%AC%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A0%95%EB%A6%AC-340f37204a27","titleInlineContent":[{"type":"text","text":"iOS 디바이스 회전 처리에 대한 정리"}],"type":"link","title":"iOS 디바이스 회전 처리에 대한 정리","identifier":"https:\/\/jongwonwoo.medium.com\/ios-%EB%94%94%EB%B0%94%EC%9D%B4%EC%8A%A4-%ED%9A%8C%EC%A0%84-%EC%B2%98%EB%A6%AC%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A0%95%EB%A6%AC-340f37204a27"},"https://developer.apple.com/documentation/uikit/uilabel":{"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uilabel","titleInlineContent":[{"type":"text","text":"UIStepper"}],"type":"link","title":"UIStepper","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uilabel"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211028_uistepper,_auto_layout,_prepare,_extension,_customstringconvertible,_nsbundle,_uibutton.json b/docs/data/documentation/arinote/211028_uistepper,_auto_layout,_prepare,_extension,_customstringconvertible,_nsbundle,_uibutton.json new file mode 100644 index 0000000..dfde574 --- /dev/null +++ b/docs/data/documentation/arinote/211028_uistepper,_auto_layout,_prepare,_extension,_customstringconvertible,_nsbundle,_uibutton.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"content":[{"text":"TIL (Today I Learned)","type":"heading","anchor":"TIL-Today-I-Learned","level":1},{"type":"paragraph","inlineContent":[{"type":"text","text":"10월 28일 (목)"}]},{"level":2,"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"inlineContent":[{"text":"오늘은 이번 스텝에서 사용해봤던 UILabel, UIButton에 대해서 정리해보고, 다음 스텝에서 사용하게 될 UIStepper에 대한 튜토리얼을 만들어보았다. 그리고 피드백이 와서 제이티와 함께 피드백에 대해서 의논하였고 리뷰어인 흰과 코드에 대해서 이야기를 나누고 싶어서 약속을 잡게되었다. 이후 오토레이아웃을 복습해보았다. 그리고 피드백 코멘트 중 이해가 안되는 부분이 있어 저녁에 리뷰어인 흰과 함께 약속을 잡아서 코드를 보면서 이야기를 나누기로 하였다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"level":2,"type":"heading","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","text":"문제점 \/ 고민한 점"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"구글링 해봤는데 은근 UIStepper에 대한 글이 별로 없었다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"오토레이아웃은 배워서 정리해본 적이 있었는데 안써먹다보니 다 까먹었다..","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"제이티와 의논하다가 prepare라는 메서드를 알고있기는 하지만 정확히 어떤 역할을 하는지 모르고 있었다는 사실을 깨달았다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"다음 스텝에서 오토 레이아웃을 설정해야하는데 프로젝트에 이미 구현되어있는 것 같아서 어떤것을 설정해야하는지 감이 오질않았는데, 디바이스를 1세대 SE로 바꿔보니 경고가 떠서 찾아보았다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"ViewController의 가독성을 높히는 방법은 뭐가 있을까?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"전역변수를 어떻게 관리하면 좋을까?","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"CustomStringConvertible의 용도를 다시한번 생각해보자.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"OK"},{"type":"text","text":"나 "},{"type":"codeVoice","code":"Cancel"},{"text":" 같은 단어를 다국어로 현지화해서 번역해주는 기능이 있는데, 어떻게 작성해야 구현할 수 있는지를 모르겠다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","type":"heading","text":"해결방법","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"Stepper","type":"codeVoice"},{"text":"에 대한 튜토리얼을 만들어보면서 사용법도 익혀보았다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"야곰의 오토 레이아웃 정복 강의를 보기 시작했다. 복습하는 느낌으로 다시 꼼꼼히 봐야겠다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"prepare라는 메서드의 예제를 만들어보았다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"경고 문구는 다음과 같았다. “set vertical compression resistance priority to 751” 그리고 스택오버플로우에서 해결방안을 찾았는데 영어가 안되서 정확히 뭔말을 하는건지는 이해를 못했다. 근데 어떤 방식으로 해결해나가야하는지 감은 잡혔다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"열심히 찾아본 결과 extension으로 ViewController를 분리해주는 방법을 알아냈다. 분류만 잘한다면 가독성 향상에 도움을 줄 것 같다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"리뷰어인 흰이 조언을 해주었는데, enum에 static 변수를 추가해서 관리하는 방식을 알아냈다.","type":"text"}],"type":"paragraph"},{"type":"codeListing","code":["private enum Const {"," static let defaultFruitCount = 10","}"],"syntax":"swift"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"난 이제까지 rawValue를 사용하기 위해서 해당 프로토콜을 쓰고 있었는데, 프로토콜의 사용의도와는 다르게 활용을 하고 있었다. 보통 인스턴스를 문자열보간법을 이용해서 String으로 반환하고 싶을 때 사용하는 프로토콜이였다. 그동안 어색한 방법으로 활용하려고 했다는 사실을 깨달았다."}]},{"syntax":"swift","code":["extension Point: CustomStringConvertible {"," var description: String {"," return \"(\\(x), \\(y))\""," }","}"],"type":"codeListing"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSBundle을 이용한다면 해결할 수 있었다. 제이티가 찾아내서 사용한 방법인데 우리 둘다 정확하게 어떤 식으로 구현되는지 간단히 읽어보기만 하였고 좀더 Bundle에 대한 타입에 대해서 공부가 필요할 것 같다."}]},{"syntax":"swift","type":"codeListing","code":["enum Text {"," case cancel"," case ok",""," var title: String {"," return localizedTitle(key: self.key)"," }",""," private var key: String {"," switch self {"," case .cancel:"," return \"Cancel\""," case .ok:"," return \"OK\""," }"," }",""," private func localizedTitle(key: String) -> String {"," let bundle = Bundle.init(for: UIButton.self)"," let title = bundle.localizedString(forKey: key, value: nil, table: nil)"," return title"," }","}"]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"heading","text":"오늘의 깨달음","level":2,"anchor":"%EC%98%A4%EB%8A%98%EC%9D%98-%EA%B9%A8%EB%8B%AC%EC%9D%8C"},{"syntax":"swift","type":"codeListing","code":["class MyButton: UIButton {"," var juice: Juice?","}","","@IBOutlet weak var orderStrawberryBananaJuiceButton: MyButton! {"," didSet {"," orderStrawberryBananaJuiceButton.juice = .strawberryBanana"," }","}"]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"위 코드는 "},{"code":"제이티","type":"codeVoice"},{"text":"가 리뷰어 ","type":"text"},{"code":"흰","type":"codeVoice"},{"type":"text","text":"이 어떤 내용으로 조언을 해준건지 이해하지 못한 나에게 코드로 설명해주려고 작성해준 코드인데, UIButton을 상속받아서 "},{"inlineContent":[{"text":"커스텀한 새로운 UIButton 타입","type":"text"}],"type":"strong"},{"text":"을 만들 수 있었다. 이런식으로 구현한다면 Switch문을 이용해서 어떤 버튼이 들어올지 형변환을 해주는 번거로움이 줄어들겠다. 버튼에 이미 어떤 쥬스버튼인지 정의되어있으니 말이다. 이 방법이 좋지 못하다고 판단해서 코드로 옮기지는 않았지만, 언젠가 UIKit에 적응되고나면 더 좋은 코드를 작성할 수 있으리라 믿는다! 열심히 공부하자!!!!","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"현업에서는 스토리보드를 잘 쓰지 않는다는 사실을 알았다. 서로 충돌나서 꼬일 뿐만 아니라, 코드로 구현하는 것이 더 직관적이고 관리하기도 편하다는 이유에서였다. 스토리보드에만 의존하지말고 코드로 작성해보는 것도 꼭 공부해야겠다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그리고 리뷰어와 코멘트로 즉, 글로 대화하는 것 보다는 시간을 맞춰서 직접 이야기를 나누는 것이 훠어어어어어얼씬!!!! 도움이 많이된다. 이것저것 꿀팁들도 많이 얻고… 앞으로 이해안가는게 있으면 적극적!!!으로 물어봐야겠다. (그동안 바쁜분 붙잡고 물어보기가 뭔가 죄송스러워서… 소극적이였다.)"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uibutton","isActive":true}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uilabel","isActive":true,"type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"overridingTitle":"UILabel","isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uibutton","overridingTitleInlineContent":[{"text":"UILabel","type":"text"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/stackoverflow.com\/questions\/54919329\/set-vertical-compression-resistance-priority-to-749-for-uitableviewcell","type":"reference","isActive":true}]}]},{"content":[{"inlineContent":[{"type":"reference","isActive":true,"identifier":"https:\/\/gwangyonglee.tistory.com\/38"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/stackoverflow.com\/questions\/6909885\/how-to-use-uikit-localized-string-in-my-app","isActive":true,"type":"reference"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"글쓰기","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"isActive":true,"identifier":"https:\/\/leeari95.tistory.com\/58","type":"reference"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/leeari95.tistory.com\/59","isActive":true}]}]}]}]}]}],"kind":"content"}],"schemaVersion":{"patch":0,"major":0,"minor":3},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211028_UIStepper,_auto_layout,_prepare,_extension,_CustomStringConvertible,_NSBundle,_UIButton"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211028_uistepper,_auto_layout,_prepare,_extension,_customstringconvertible,_nsbundle,_uibutton"]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"kind":"article","sections":[],"metadata":{"roleHeading":"Article","title":"211028 UIStepper, auto layout, prepare, extension, CustomStringConvertible, NSBundle, UIButton","role":"article","modules":[{"name":"Ari's Notes"}]},"references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://leeari95.tistory.com/58":{"type":"link","titleInlineContent":[{"type":"text","text":"UIStepper Tutorial"}],"identifier":"https:\/\/leeari95.tistory.com\/58","url":"https:\/\/leeari95.tistory.com\/58","title":"UIStepper Tutorial"},"https://gwangyonglee.tistory.com/38":{"type":"link","titleInlineContent":[{"text":"extension으로 코드 가독성 올리기","type":"text"}],"identifier":"https:\/\/gwangyonglee.tistory.com\/38","url":"https:\/\/gwangyonglee.tistory.com\/38","title":"extension으로 코드 가독성 올리기"},"https://leeari95.tistory.com/59":{"type":"link","titleInlineContent":[{"type":"text","text":"prepare"}],"identifier":"https:\/\/leeari95.tistory.com\/59","url":"https:\/\/leeari95.tistory.com\/59","title":"prepare"},"https://stackoverflow.com/questions/54919329/set-vertical-compression-resistance-priority-to-749-for-uitableviewcell":{"type":"link","titleInlineContent":[{"text":"set vertical compression resistance priority…","type":"text"}],"identifier":"https:\/\/stackoverflow.com\/questions\/54919329\/set-vertical-compression-resistance-priority-to-749-for-uitableviewcell","url":"https:\/\/stackoverflow.com\/questions\/54919329\/set-vertical-compression-resistance-priority-to-749-for-uitableviewcell","title":"set vertical compression resistance priority…"},"https://developer.apple.com/documentation/uikit/uibutton":{"type":"link","titleInlineContent":[{"type":"text","text":"UIButton"}],"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uibutton","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uibutton","title":"UIButton"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://developer.apple.com/documentation/uikit/uilabel":{"type":"link","titleInlineContent":[{"text":"UIStepper","type":"text"}],"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uilabel","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uilabel","title":"UIStepper"},"https://stackoverflow.com/questions/6909885/how-to-use-uikit-localized-string-in-my-app":{"type":"link","titleInlineContent":[{"type":"text","text":"Bundle로 다국어 지원기능 구현"}],"identifier":"https:\/\/stackoverflow.com\/questions\/6909885\/how-to-use-uikit-localized-string-in-my-app","url":"https:\/\/stackoverflow.com\/questions\/6909885\/how-to-use-uikit-localized-string-in-my-app","title":"Bundle로 다국어 지원기능 구현"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211029_viewcontroller,_life_cycle,_notificationcenter,_deinit,_bundle,_preview.json b/docs/data/documentation/arinote/211029_viewcontroller,_life_cycle,_notificationcenter,_deinit,_bundle,_preview.json new file mode 100644 index 0000000..eb607a4 --- /dev/null +++ b/docs/data/documentation/arinote/211029_viewcontroller,_life_cycle,_notificationcenter,_deinit,_bundle,_preview.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"content":[{"level":1,"type":"heading","anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)"},{"inlineContent":[{"text":"10월 29일 (금)","type":"text"}],"type":"paragraph"},{"level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용","type":"heading"},{"inlineContent":[{"text":"오늘은 ViewController의 Life Cycle에 대한 활동학습을 진행하였다. 혼자 예습삼아 공부했던 것보다 팀원들과 다양한 이야기를 나누며 훨씬 많은 것들을 깨닫는 유익한 시간이였다. 이후에는 오토레이아웃 강의를 다시 보기 시작했다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":" "}],"type":"paragraph"},{"text":"문제점 \/ 고민한 점","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","type":"heading","level":2},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"NotificationCenter의 ","type":"text"},{"type":"codeVoice","code":"removeObserver"},{"text":"를 어디서 하면 좋은걸까?","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"modal을 켰을 때의 Life Cycle이 어떤 방식으로 동작하지?","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"모달을 내릴까 말까 하는 모션을 취할때 Life Cycle의 동작방식은 어떻게 될까?","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"오토레이아웃을 보다가 Preiview에 대한 것을 알게되서 찾아보다가 "},{"type":"codeVoice","code":"UIKit에서도 실시간 Preiview 기능"},{"type":"text","text":"을 사용할 수 있다는 점을 찾아보게 되었다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Bundle"},{"text":"이란?","type":"text"},{"text":" ","type":"text"},{"type":"text","text":" "}]}]}]},{"text":"해결방법","level":2,"type":"heading","anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"팀원들과 의논 끝에 "},{"code":"deinit","type":"codeVoice"},{"type":"text","text":"에다가 remove하면 적절할 것 같다고 이야기를 나누었다. "},{"code":"이유","type":"codeVoice"},{"type":"text","text":"는 화면이 잠깐 modal로 다른곳으로 세어나가도 noti를 받아서 view에 대한 업데이트가 필요할 경우에는 View가 잠깐 사라져도 옵저버는 유지되어야 한다는 이유에서 그렇게 결론이 났다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"modal로 뷰가 넘어갈때에는 Navigation과는 다르게 이전 뷰가 사라지지 않고 남아있는 상태에서 NextView가 didAppear되었다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"text":"Disappearing 상태는 두가지 경우로부터 만들어진다.","type":"heading","anchor":"Disappearing-%EC%83%81%ED%83%9C%EB%8A%94-%EB%91%90%EA%B0%80%EC%A7%80-%EA%B2%BD%EC%9A%B0%EB%A1%9C%EB%B6%80%ED%84%B0-%EB%A7%8C%EB%93%A4%EC%96%B4%EC%A7%84%EB%8B%A4","level":3},{"type":"paragraph","inlineContent":[{"text":"새로운 ViewController가 등장하여 현재의 화면을 덮는 경우","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"이전화면으로 돌아가기 위해 현재의 ViewController가 사라지는 경우"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"modal을 내릴듯 말듯한 제스처를 취해볼 생각을 못해서 신기했다. "},{"code":"ViewWillDisappear","type":"codeVoice"},{"text":"와 ","type":"text"},{"code":"ViewWillAppear","type":"codeVoice"},{"text":",","type":"text"},{"code":"ViewDidAppear","type":"codeVoice"},{"text":"가 번갈아가며 호출되는 과정을 보면서 Life Cycle에 대한 이해도가 높아지는 것을 느꼈다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"야곰의 오토레이아웃 강의를 보다가 preview를 보는 방법을 알게되었다.","type":"text"},{"type":"text","text":" "},{"identifier":"https:\/\/i.imgur.com\/B3W7GfY.gif","type":"image"},{"text":" ","type":"text"},{"identifier":"https:\/\/i.imgur.com\/Y8143ov.gif","type":"image"},{"type":"text","text":" "},{"type":"text","text":"근데 위 방법은 코드로 작성한 부분은 적용이 되지가 않는다."},{"type":"text","text":" "},{"code":"SwiftUI","type":"codeVoice"},{"type":"text","text":"은 "},{"code":"UIKit","type":"codeVoice"},{"type":"text","text":"와 다르게 코드를 작성하면서 실시간으로 Preview를 볼 수 있다는 것이 기억이나서 구글링 해보다가 UIKit에서도 실시간으로 Preview를 보면서 화면을 구현할 수 있다는 사실을 알게되었다. 훑어보니 "},{"code":"SwiftUI","type":"codeVoice"},{"type":"text","text":"를 import해서 "},{"type":"codeVoice","code":"UIViewRepresentable, PreviewProvider"},{"text":" 두개의 프로토콜을 이용하여 Preview를 구현할 수 있는 방법이었다. 아직은 코드가 잘 이해가 가지 않기도 하고, 코드로 화면을 구현하는 방법이 익숙하지 않아서 나중에 스토리보드 말고 only code로 화면을 구현하게되는 날에 다시한번 써먹어보면 좋을 것 같다는 생각이 들었다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"알고보니 한달전에 이미 Bundle에 대해서 "},{"isActive":true,"identifier":"https:\/\/leeari95.tistory.com\/46","type":"reference"},{"text":"이 있었다. (어쩐지.. 익숙하더라니… 왜 기억에없지….)다시보면서 복습을 해보았다.","type":"text"},{"text":" ","type":"text"},{"text":" ","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"reference","isActive":true,"identifier":"https:\/\/www.avanderlee.com\/xcode\/xcode-previews\/"}],"type":"paragraph"}]}]}]}]}],"kind":"content"}],"schemaVersion":{"major":0,"patch":0,"minor":3},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211029_ViewController,_Life_Cycle,_NotificationCenter,_deinit,_Bundle,_preview","interfaceLanguage":"swift"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211029_viewcontroller,_life_cycle,_notificationcenter,_deinit,_bundle,_preview"]}],"sections":[],"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"metadata":{"modules":[{"name":"Ari's Notes"}],"role":"article","title":"211029 ViewController, Life Cycle, NotificationCenter, deinit, Bundle, preview","roleHeading":"Article"},"references":{"https://i.imgur.com/B3W7GfY.gif":{"identifier":"https:\/\/i.imgur.com\/B3W7GfY.gif","variants":[{"url":"https:\/\/i.imgur.com\/B3W7GfY.gif","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/Y8143ov.gif":{"identifier":"https:\/\/i.imgur.com\/Y8143ov.gif","variants":[{"url":"https:\/\/i.imgur.com\/Y8143ov.gif","traits":["1x","light"]}],"alt":null,"type":"image"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://leeari95.tistory.com/46":{"identifier":"https:\/\/leeari95.tistory.com\/46","titleInlineContent":[{"type":"text","text":"공부한 기록"}],"title":"공부한 기록","url":"https:\/\/leeari95.tistory.com\/46","type":"link"},"https://www.avanderlee.com/xcode/xcode-previews/":{"identifier":"https:\/\/www.avanderlee.com\/xcode\/xcode-previews\/","titleInlineContent":[{"type":"text","text":"Xcode Previews"}],"title":"Xcode Previews","url":"https:\/\/www.avanderlee.com\/xcode\/xcode-previews\/","type":"link"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211030_auto_layout.json b/docs/data/documentation/arinote/211030_auto_layout.json new file mode 100644 index 0000000..56c6d32 --- /dev/null +++ b/docs/data/documentation/arinote/211030_auto_layout.json @@ -0,0 +1 @@ +{"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211030_Auto_Layout","interfaceLanguage":"swift"},"variants":[{"paths":["\/documentation\/arinote\/211030_auto_layout"],"traits":[{"interfaceLanguage":"swift"}]}],"primaryContentSections":[{"content":[{"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","type":"heading","level":1},{"inlineContent":[{"type":"text","text":"10월 30일 (토)"}],"type":"paragraph"},{"type":"heading","level":2,"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"paragraph","inlineContent":[{"text":"오늘은 오토레이아웃을 복습하면서 알게된 새로운 지식에 대해서 정리해보았다.","type":"text"}]},{"level":1,"anchor":"Intrinsic-Content-Size","text":"Intrinsic Content Size","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Content의 기본적인 크기를 뜻한다."}]},{"text":"Placeholder","anchor":"Placeholder","type":"heading","level":2},{"type":"paragraph","inlineContent":[{"text":"스토리보드 내에서만 임시적으로 크기를 조정할 수 있다. 실제 앱 실행시에는 적용한 사이즈가 반영되지 않는다.","type":"text"}]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"코드로 UIView의 사이즈를 지정해주고, class를 연결해주면 사이즈를 갖는 View가 구성된다.","type":"text"}]},{"syntax":"swift","type":"codeListing","code":["@IBDesignable","class MyView: UIView {",""," override var intrinsicContentSize: CGSize {"," return CGSize(width: 50, height: 50)"," }","}"]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"level":1,"type":"heading","text":"CHCR","anchor":"CHCR"},{"type":"paragraph","inlineContent":[{"type":"text","text":"자신의 사이즈를 지키려는 힘을 뜻한다. Priority를 가지고 그것을 기반으로 힘의 세기를 결정하게 된다."}]},{"level":2,"text":"Content Hugging","type":"heading","anchor":"Content-Hugging"},{"type":"paragraph","inlineContent":[{"text":"외부에서 압력을 줄 때 늘어나지 않으려고 버티는 힘","type":"text"}]},{"text":"예제로 이해하기","anchor":"%EC%98%88%EC%A0%9C%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0","level":2,"type":"heading"},{"items":[{"content":[{"inlineContent":[{"text":"예시로 레이블의 Content Hugging을 각각 다른 강도로 주어졌을때 250으로 세팅되어있는 레이블이 늘어난 것을 확인할 수 있다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"1000은 절대 늘어나지 않고, 750은 조금 늘어나도 괜찮고, 250은 늘어나도 상관없다는 뜻"}]}]}]}]}],"type":"unorderedList"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/HBs4Xtn.png","type":"image"}],"type":"paragraph"},{"anchor":"Compression-Resistance","text":"Compression Resistance","level":2,"type":"heading"},{"type":"paragraph","inlineContent":[{"text":"외부에서 압력을 줄 때 버티는 힘","type":"text"}]},{"text":"예제로 이해하기","type":"heading","anchor":"%EC%98%88%EC%A0%9C%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0","level":2},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Priority가 가장 낮은 레이블은 아무리 늘어나려고 해도 옆에 Priority가 높은 레이블이 있기 때문에 더 이상 늘어나지 않는 것을 확인할 수 있다.","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/e5gK1Sv.png","type":"image"},{"type":"text","text":" "},{"text":" ","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"반대로 Priority가 높은 레이블이 늘어나려고 한다면 Priority가 낮은 250 레이블이 덮어버리는 현상을 확인할 수 있다.","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/y8C00rs.png"},{"type":"text","text":" "},{"text":" ","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"아래는 제일 높은 우선순위(Priority)를 가진 레이블이 다 덮어버린 모습이다."}]}]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/794jWfF.png","type":"image"},{"type":"text","text":" "},{"type":"text","text":" "}],"type":"paragraph"},{"level":1,"type":"heading","text":"꿀팁모음","anchor":"%EA%BF%80%ED%8C%81%EB%AA%A8%EC%9D%8C"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"메뉴가 펼쳐진 상태에서 Option키를 누르면 메뉴에 옵션이 추가되는 것을 볼 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/st3n9FP.gif"},{"text":" ","type":"text"},{"text":" ","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"www.wtfautolayout.com","isActive":true,"type":"reference"},{"type":"text","text":"에 오토 레이아웃 오류 로그를 붙여넣으면 어떻게 해결해야하는지 힌트들이 나온다."}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/Mq39Dcu.gif","type":"image"}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"}],"kind":"content"}],"sections":[],"metadata":{"title":"211030 Auto Layout","roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}]},"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"schemaVersion":{"minor":3,"patch":0,"major":0},"references":{"https://i.imgur.com/794jWfF.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/794jWfF.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/794jWfF.png"}]},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/st3n9FP.gif":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/st3n9FP.gif","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/st3n9FP.gif"}]},"https://i.imgur.com/Mq39Dcu.gif":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/Mq39Dcu.gif","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/Mq39Dcu.gif"}]},"www.wtfautolayout.com":{"title":"해당 사이트","type":"link","titleInlineContent":[{"type":"text","text":"해당 사이트"}],"identifier":"www.wtfautolayout.com","url":"www.wtfautolayout.com"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/e5gK1Sv.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/e5gK1Sv.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/e5gK1Sv.png"}]},"https://i.imgur.com/HBs4Xtn.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/HBs4Xtn.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/HBs4Xtn.png"}]},"https://i.imgur.com/y8C00rs.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/y8C00rs.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/y8C00rs.png"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211101_kvo,_property_observer,_prepare,_loadviewifneeded,_nsobject,_uml,_auto_layout.json b/docs/data/documentation/arinote/211101_kvo,_property_observer,_prepare,_loadviewifneeded,_nsobject,_uml,_auto_layout.json new file mode 100644 index 0000000..3d99400 --- /dev/null +++ b/docs/data/documentation/arinote/211101_kvo,_property_observer,_prepare,_loadviewifneeded,_nsobject,_uml,_auto_layout.json @@ -0,0 +1 @@ +{"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211101_KVO,_Property_Observer,_prepare,_loadViewIfNeeded,_NSObject,_UML,_Auto_Layout"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"title":"211101 KVO, Property Observer, prepare, loadViewIfNeeded, NSObject, UML, Auto Layout","roleHeading":"Article"},"schemaVersion":{"major":0,"patch":0,"minor":3},"kind":"article","sections":[],"variants":[{"paths":["\/documentation\/arinote\/211101_kvo,_property_observer,_prepare,_loadviewifneeded,_nsobject,_uml,_auto_layout"],"traits":[{"interfaceLanguage":"swift"}]}],"primaryContentSections":[{"content":[{"type":"heading","anchor":"TIL-Today-I-Learned","level":1,"text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"type":"text","text":"11월 1일 (월)"}]},{"type":"heading","text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"inlineContent":[{"type":"text","text":"오늘은 오전에 야곰이 KVO와 Property Observer에 대한 녹화를 진행한다고 하여 참석해서 학습을 했다. 이후 오토레이아웃을 마저 학습하고 오후에는 제이티와 함께 STEP 3를 어떻게 구현해볼지 의논해보는 시간을 가졌다. 이후에 UML에 대해서 활동학습을 진행했다. 프로젝트 리뷰어인 흰이 강의해주었다!"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"type":"heading","level":2,"anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","text":"문제점 \/ 고민한 점"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"옵저빙하는 객체가 private이면 옵저빙이 가능할까?"}]}]},{"content":[{"inlineContent":[{"text":"KVO와 Property Observer는 언제 쓰면 적절할까?","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"왜 텍스트 필드가 왜저리 늘어나는거지….쩝","type":"text"}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/MrPgtP1.png"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"prepare 메소드로 넘기기 전에 view를 load하는 방법?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"STEP 3를 진행하는 도중에 왜 만들었는지 모르겠는 코드를 발견했다.. 헐"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"UML 강의를 듣고, 예습도 하고 갔지만 화살표의 정확한 용도가 잘 이해가 가지 않았다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"NSObject에 대해서 알아보자"}],"type":"paragraph"}]}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"text":"해결방법","anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","level":2,"type":"heading"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"오.. 불가능했다. 이런 고민은 해본 적 없었는데, 캠퍼덕에 새로운 사실을 알게되었네!"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"KVO는 타입 정의 외부에서 옵저버를 추가할 때 사용할 수 있고, Property Observer는 타입 정의 내부에서 사용할 수 있다. 때에 따라 적절히 이용해주면 좋을 것 같다는 생각이 들었다.","type":"text"}],"type":"paragraph"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Ln5xGmo.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 부분을 놓쳐서 그랬었다. 근데 이걸 해결했는데 또다른 문제가 생겼다… Scroll View 안에 있는 Stack View가 늘어나지가 않아서 스크롤도 안생기는 현상이 생겨서 자세히 살펴보았더니 Stack View의 Width가 잘못 설정이 되어있었다. priority가 설정이 안되어 있었고, 잘못 설정해준걸로 착각한 위 제약때문에 삭제해주었더니 해결되었다."}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/gxIT2hn.png"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/cxDxkwX.gif"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"제이티가 공식문서를 찾다가 발견해주었다. loadViewIfNeeded() 라는 메소드인데 아직 로드되지 않는 경우 뷰 컨트롤러의 뷰를 로드하는 메소드이다. 근데 이렇게 수동적으로 view를 미리 로드하는게 적절한지 잘모르겠다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"내일 제이티와 기억을 같이 더듬어봐야할 것 같다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"질문시간에 흰에게 질문하였다. 프로토콜과 익스텐션의 관계를 실체화라고 봐야하는지 의존으로 봐야하는지 감이 안왔다. 흰의 답변은 둘다 적절하지 않다는 답변이였다. 나중에 흰의 UML도 보았는데, 익스텐션을 따로 만들어주지 않고 그냥 프로토콜 내부에 통합해주었다. 다른 예제들을 찾아보면 따로 만들어준 경우도 있고… 따라서 정답은 없는 것 같다. UML의 목적은 곧 의사소통이므로 커뮤니케이션 하는데 있어서 문제만 없게 적절하게 그리는 것이 관건이겠다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"내가 그려본 UML"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/dEYLhwy.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSObject는 다른 클래스를 상속받지 않는 루트 클래스라고 한다. NSObject와 이름이 같은 프로토콜의 공식문서를 살펴보면 모든 Objective-C 객체의 근간이 되는 메서드를 모아둔 프로토콜이라고 한다. 설명중에서 코코아, 런타임 시스템이라는 말과 Objective-C는 왜 등장하는지가 궁금해지는데, 간단히 요약하자면 코코아 터치는 iOS 앱개발을 위한 UiKit, Foundation 등의 프레임워크를 포함한 최상위 프레임워크이고, 그 중 Foundation 프레임워크 내부에는 NSObject가 정의되어 있다. Foundation에서 정의한 데이터 타입은 NSString, NSArray와 같이 앞에 NS가 붙어있고, 이유는 잡스가 과거에 개발했던 운영체제의 이름이 NeXTSTEP이기 때문에 프레임워크에 NS로 흔적이 남아있는 것이다."}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"type":"heading","anchor":"%EA%B3%B5%EB%B6%80%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC","text":"공부내용 정리","level":2},{"inlineContent":[{"type":"text","text":"Unified Modeling Language의 약자"},{"text":" ","type":"text"},{"type":"text","text":"도메인을 모델로 표현해주는 대표적인 모델링 언어"},{"type":"text","text":" "},{"type":"text","text":"소프트웨어 시스템, 업무 모델링, 시스템의 산출물을 규정하고 시각화, 문서화하는 언어"},{"type":"text","text":" "},{"type":"text","text":"프로그램 언어가 아닌 기호와 도식을 이용하여 표현하는 방법을 정의한다."},{"type":"text","text":" "},{"type":"text","text":"작성목적은 객체지향 시스템을 가시화, 명세화, 문서화한다."},{"text":" ","type":"text"},{"type":"text","text":"소프트웨어를 설게하며 아래와 같은 필요에 의해서 사용된다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"의사소통 또는 설계 논의를 위해 (같이 일하는 사람)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"전체 시스템의 구조 및 클래스의 의존성 파악을 위해 (나 자신)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"유지보수를 위한 설계의 back-end 문서 제작을 위해 (문서화)","type":"text"}]}]}],"type":"unorderedList"},{"type":"heading","text":"목표","anchor":"%EB%AA%A9%ED%91%9C","level":1},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자들이 의미있는 모델을 만들고 공유할 수 있도록 사용하기 쉬우면서도 표현이 풍부한 시각적 모형화 언어를 제공한다. (문서의 직관성을 높인다)"}]}]},{"content":[{"inlineContent":[{"text":"핵심 개념을 확장하기 위한 메커니즘을 제공한다","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"특정 프로그래밍 언어나 개발 공정에 종속되지 않아야 한다. (통합성을 중요시)"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"모델링 언어를 이해하기 위한 공식적 기준을 제공"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"객체지향 도구 시장의 성장을 장려해야 한다."}]}]},{"content":[{"inlineContent":[{"text":"협동, 프레임워크, 패턴, 컴포넌트 등의 고수준의 개발 개념들을 지원한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"산업계의 검증된 최상의 경험들을 통합한다."}],"type":"paragraph"}]}]},{"text":"다이어그램 종류","anchor":"%EB%8B%A4%EC%9D%B4%EC%96%B4%EA%B7%B8%EB%9E%A8-%EC%A2%85%EB%A5%98","level":1,"type":"heading"},{"items":[{"content":[{"inlineContent":[{"text":"Use Case 요구 분석 과정에서 시스템과 외부와의 상호 작용을 묘사하ㅓㅁ","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Activity 업무의 흐름을 모델링하거나 객체의 생명주기를 표현함"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Sequence 객체간의 메세지 전달을 시간적 흐름에서 분석함"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Collaboration 객체와 객체가 주고받는 메세지 중심의 작성함"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Class 시스템의 구조적인 모습을 그림","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Component 소프트웨어 구조를 그림","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Deployment 기업 환경의 구성과 컴포넌트들 간의 관계를 그림","type":"text"}]}]}],"type":"unorderedList"},{"text":"클래스 다이어그램","level":1,"type":"heading","anchor":"%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%8B%A4%EC%9D%B4%EC%96%B4%EA%B7%B8%EB%9E%A8"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/KMbcAmd.png"},{"text":" ","type":"text"},{"identifier":"https:\/\/i.imgur.com\/jBdIYkK.png","type":"image"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"정적 다이어그램으로 클래스의 구성요소 및 클래스간의 관계를 표현하는 대표적인 UML","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"시스템의 일부 또는 전체의 구조를 나타낼 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"의존관계를 명확히 보게 해주며 순환 의존이 발생하는 지점을 찾아내서 어떻게 이 순환고리를 깰 수 있을지 결정할 수 있게 해준다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"상단 섹션: 클래스 이름","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"중간 섹션: 클래스 속성"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하단 섹션: 클래스 메서드 또는 작업"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"anchor":"%EC%9A%A9%EB%8F%84","level":2,"text":"용도","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"문제 해결을 위한 도메인 구조를 나타내어 보이지 않는 도메인 안의 개념과 같은 추상적인 개념을 기술하기 위해 사용"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"소프트웨어의 설계 혹은 완성된 소프트웨어의 구현 설명을 목적으로 사용"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/AjhHOxd.png"}]},{"inlineContent":[{"type":"text","text":"클래스 다이어그램의 활용"}],"type":"paragraph"},{"text":"기본 요소","level":2,"type":"heading","anchor":"%EA%B8%B0%EB%B3%B8-%EC%9A%94%EC%86%8C"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/Dtruyfp.png","type":"image"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"접근제어자"},{"type":"text","text":" "},{"text":"Public +","type":"text"},{"type":"text","text":" "},{"type":"text","text":"Private -"},{"text":" ","type":"text"},{"type":"text","text":"Protected #"}]},{"inlineContent":[{"type":"text","text":"속성"},{"text":" ","type":"text"},{"text":"접근제어자 이름: 타입 = 기본값","type":"text"},{"type":"text","text":" "},{"type":"text","text":"Ex) -title: String = “”"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"메서드"},{"type":"text","text":" "},{"text":"접근제어자 이름(파라미터 속성): 리턴값","type":"text"},{"text":" ","type":"text"},{"text":"Ex) +setTitle(String)","type":"text"},{"type":"text","text":" "},{"type":"text","text":"Ex) +getTittle(): String"}]},{"text":"클래스 다이어그램을 이용한 관계 표현","type":"heading","level":2,"anchor":"%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%8B%A4%EC%9D%B4%EC%96%B4%EA%B7%B8%EB%9E%A8%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EA%B4%80%EA%B3%84-%ED%91%9C%ED%98%84"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/O8T3VPE.png","type":"image"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"일반화는 상속을 하는 클래스를 가리킬때 사용할 수 있다"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"의존은 연관관계가 있는 클래스를 가리킬 때 사용할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"집합관계는 전체객체, 부분객체로 구분할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"합성관계는 라이프사이클이 연관되어있어서 강한 집합관계를 뜻한다"}]}]}]},{"anchor":"Sequence-%EB%8B%A4%EC%9D%B4%EC%96%B4%EA%B7%B8%EB%9E%A8","level":1,"type":"heading","text":"Sequence 다이어그램"},{"inlineContent":[{"type":"text","text":"순서를 파악하고 싶을 때 사용한다."},{"type":"text","text":" "},{"identifier":"https:\/\/i.imgur.com\/8M0ntrG.png","type":"image"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"단하고 안전한 방법중에 하나인데 한계점이 있다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"Auto Layout을 설정할 때 Anchor를 사용하면 좋은점은 코드가 굉장히 간결하다는 것이다. 그리고 코드 컴파일 타임에 간단하게 오류를 체크할 수 있다는 점이다."}]},{"text":"translatesAutoresizingMaskIntoConstraints","type":"heading","level":2,"anchor":"translatesAutoresizingMaskIntoConstraints"},{"inlineContent":[{"type":"text","text":"Auto Layout을 사용하여 View의 크기와 위치를 동적으로 계산하려면 이 프로퍼티를 false로 설정한 다음 View에 모호(ambiguous)하지 않고 충돌하지 않는(Nonconflicting) Constraint 집합을 제공해야 한다."}],"type":"paragraph"},{"syntax":"swift","type":"codeListing","code":["class AnchorViewController: UIViewController {"," "," override func viewDidLoad() {"," super.viewDidLoad()"," "," let button = UIButton()"," button.setTitle(\"Button\", for: .normal)"," button.setTitleColor(.white, for: .normal)"," button.backgroundColor = .systemGreen"," view.addSubview(button)"," "," button.translatesAutoresizingMaskIntoConstraints = false"," "," \/\/ safeArea의 영역을 나타내주는 가이드"," let safeArea = view.safeAreaLayoutGuide"," "," \/\/ constraint를 활성화 해주는 구간. 활성화를 해주지 않으면 무력화가 된다."," button.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor, constant: 16).isActive = true"," button.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor, constant: -16).isActive = true"," \/\/ 코드로 제약을 줄 때는 양수, 음수 어떤 개념으로 들어올지 생각해보고 작성해야한다."," let safeBottomAnchor = button.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor)"," safeBottomAnchor.isActive = true"," safeBottomAnchor.priority = .init(999)"," let viewBottomAnchor = button.bottomAnchor.constraint(lessThanOrEqualTo: view.bottomAnchor, constant: -20)"," viewBottomAnchor.isActive = true"," "," }"," ","}"]},{"type":"paragraph","inlineContent":[{"type":"text","text":"상세하게 오토레이아웃을 설정해줄 수 있지만 컴파일시 오류를 검출해주지 않는다는 단점이 있다. 그래서 Anchor를 사용하면서 오토레이아웃이 적용이 안되는, 한계점이 왔을 때만 사용해주는 것이 좋다."}]},{"syntax":"swift","code":["let safeArea = view.safeAreaLayoutGuide"," let leading = NSLayoutConstraint(item: button,"," attribute: .leading,"," relatedBy: .equal,"," toItem: safeArea,"," attribute: .leading,"," multiplier: 1,"," constant: 16)"," let trailing = NSLayoutConstraint(item: button,"," attribute: .trailing,"," relatedBy: .equal,"," toItem: safeArea,"," attribute: .trailing,"," multiplier: 1,"," constant: -16)"," let bottomSafeArea = NSLayoutConstraint(item: button,"," attribute: .bottom,"," relatedBy: .equal,"," toItem: safeArea,"," attribute: .bottom,"," multiplier: 1,"," constant: -16)"," bottomSafeArea.priority = .defaultHigh"," let bottomView = NSLayoutConstraint(item: button,"," attribute: .bottom,"," relatedBy: .lessThanOrEqual,"," toItem: view,"," attribute: .bottom,"," multiplier: 1,"," constant: -20)"," NSLayoutConstraint.activate([leading, trailing, bottomView, bottomSafeArea])"],"type":"codeListing"},{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/library\/archive\/documentation\/UserExperience\/Conceptual\/AutolayoutPG\/Size-ClassSpecificLayout.html#\/\/apple_ref\/doc\/uid\/TP40010853-CH26-SW1","isActive":true,"type":"reference"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/Mo1HvY1.png","type":"image"}]},{"type":"paragraph","inlineContent":[{"text":"크기에 따라 content area에 자동적으로 할당되는 특성을 의미한다.","type":"text"},{"type":"text","text":" "},{"text":"즉 시스템은 View의 높이와 너비를 설명하는 regulart, compact의 두가지 크기 클래스를 정의한다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":"그러나 사실상 이것을 꼭 필요로 하는 앱이 아니면 현업에서는 잘 사용하지 않는 기능이기도 하다."},{"text":" ","type":"text"},{"text":"대체적으로 아이폰이나 아이패드를 스토리보드를 별도로 작업하는 경우가 많다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 View는 다음과 같은 4가지 조합의 크기 클래스를 가질 수 있다."}]},{"items":[{"content":[{"inlineContent":[{"text":"Regular width, regular height","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Regular width, compact height"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Compact width, regular height"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Compact width, compact height","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 iOS는 content area의 크기 클래스를 사용자가 장치를 가로방향에서 세로방향으로 회전하면 수직크키 클래스가 compact -> regular로 변경되며 tab bar의 막대가 높아지는 것과 같이 동적으로 layout을 조정하게 된다."}]},{"inlineContent":[{"text":"어떤기기가 어떤 크기를 갖게 되는지는 ","type":"text"},{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/visual-design\/adaptivity-and-layout\/"},{"type":"text","text":"에 스펙이 자세히 나와있다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/F7HaWjY.png"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Scroll View를 추가하고 제약조건을 추가해줘도 빨간 경고가 뜬다. 이유가 뭘까?
그것은 바로 ‘Scroll View안에 들어갈 Content의 사이즈가 지정이 되지 않았다’ 라는 경고다.
따라서 Scroll View 안에 Content View를 추가해주고 제약을 준다면 해결된다."}]}]}],"type":"unorderedList"},{"type":"heading","text":"Content Layout Guide와 Frame Layout Guide","anchor":"Content-Layout-Guide%EC%99%80-Frame-Layout-Guide","level":2},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/sx1zDGK.gif"}]},{"level":3,"anchor":"Content-Layout-Guide","type":"heading","text":"Content Layout Guide"},{"inlineContent":[{"type":"text","text":"들어오는 컨텐츠에 따라 레이아웃을 변경시켜줄테니 컨텐츠 크기를 나에게 알려줘"}],"type":"paragraph"},{"text":"Frame Layout Guide","type":"heading","anchor":"Frame-Layout-Guide","level":3},{"type":"paragraph","inlineContent":[{"type":"text","text":"스크롤과 관계없이 화면 안에 고정되어야 할 요소가 있어? 나에게 위치를 알려줘"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Content Layout Guide에 따라 늘어난 Label의 크기","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/3TB3CiV.gif"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Frame Layout Guide와 관계를 맺어 고정되어있는 Label","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/tlBQ8Am.png","type":"image"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Scroll View와 Stack View를 활용한 예제","type":"text"}],"type":"paragraph"}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/luejd48.png"}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":[" @IBAction func addView() {"," let label = UILabel()"," label.font = UIFont.preferredFont(forTextStyle: .largeTitle)"," label.isHidden = true"," label.text = \"\"\""," dasdasd"," asdasdasd"," asda"," dasd"," asdasdsadas"," asdasdasdasd"," asdasdasdas"," asdasdasdasd"," \"\"\""," label.numberOfLines = 0"," label.adjustsFontForContentSizeCategory = true"," stackView.addArrangedSubview(label)"," "," UIView.animate(withDuration: 0.3) {"," label.isHidden = false"," }"," }"," "," @IBAction func removeView() {"," guard let last = stackView.arrangedSubviews.last else { return }"," "," UIView.animate(withDuration: 0.3) {"," last.isHidden = true"," } completion: { _ in"," self.stackView.removeArrangedSubview(last)"," }"," }"]},{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/visual-design\/typography\/","isActive":true}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/KGL4YuW.png","type":"image"}]},{"type":"paragraph","inlineContent":[{"text":"모든 사용자가 시력이 같진 않다. 어떤 사용자는 글씨를 크게 키워서 앱을 이용할 수도 있는데, 우리가 앱을 개발할때 font 크기를 지정해버리면 사용자가 아무리 글씨를 크게 키운다고 해도 앱의 font 크기는 그대로일 것이다. 그래서 그런 요구사항을 유연하게 충족할 수 있도록 Dynamic Type을 사용한다면 훨씬 더 접근성을 구현하기가 좋아질 수 있겠다.","type":"text"}]},{"text":"예제를 통해 알아보기","level":2,"type":"heading","anchor":"%EC%98%88%EC%A0%9C%EB%A5%BC-%ED%86%B5%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"레이블을 추가하여 임의의 크기를 지정해보자","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/v0hFYIf.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Accessibility Inspector를 열어준다.","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/4msnSRe.gif","type":"image"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"그리고 아래와 같이 설정해주면 Dynamic Type 크기를 설정할 수 있는 설정으로 갈 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/BQDWk8O.gif","type":"image"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Dynamic Type의 크기를 변경해주면 임의로 크기를 지정한 Label은 크기가 변하지 않는 것을 확인할 수 있다."}]}]}],"type":"unorderedList"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/smR4FRt.gif","type":"image"}],"type":"paragraph"},{"text":"Dynamic Type 적용해보기","type":"heading","anchor":"Dynamic-Type-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"아래와 같이 Label의 설정을 바꿔준다. 폰트 크기를 Dynamic Type으로 설정해준다. Automatically Adjusts Font는 앱이 실행되는 동안에 글씨 크기가 바꾸는 것을 허용하는지의 대한 것이다. 체크를 해주지 않으면 앱을 끄고 다시켜서 폰트가 커졌는지 확인해야하는 번거로움이 생기기 때문에 체크를 해주는 것이다.","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/1w4TvUT.gif"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"앱을 실행해서 확인해보면 폰트 크기가 변하는 것을 확인할 수 있다.","type":"text"}]}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/CE1FK1F.gif"}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["\/\/ NotificationCenter를 이용하여 Button들의 Label 크기를 Dynamic Type으로 정의하기"," @IBOutlet var buttons: [UIButton]!"," "," override func viewDidLoad() {"," super.viewDidLoad()",""," NotificationCenter.default.addObserver(self, selector: #selector(adjustButtonDynamicType), name: UIContentSizeCategory.didChangeNotification, object: nil)"," \/\/ Do any additional setup after loading the view."," }"," "," @objc func adjustButtonDynamicType() {"," buttons.forEach { (button) in"," button.titleLabel?.adjustsFontForContentSizeCategory = true"," }"," }"]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"items":[{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621446-loadviewifneeded","isActive":true}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/github.com\/MarcoEidinger\/SwiftPlantUML","type":"reference","isActive":true}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/gmlwjd9405.github.io\/2018\/07\/04\/class-diagram.html","isActive":true}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}],"kind":"content"}],"references":{"https://i.imgur.com/KMbcAmd.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/KMbcAmd.png","variants":[{"url":"https:\/\/i.imgur.com\/KMbcAmd.png","traits":["1x","light"]}]},"https://i.imgur.com/sx1zDGK.gif":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/sx1zDGK.gif","variants":[{"url":"https:\/\/i.imgur.com\/sx1zDGK.gif","traits":["1x","light"]}]},"https://i.imgur.com/Dtruyfp.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/Dtruyfp.png","variants":[{"url":"https:\/\/i.imgur.com\/Dtruyfp.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/MrPgtP1.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/MrPgtP1.png","variants":[{"url":"https:\/\/i.imgur.com\/MrPgtP1.png","traits":["1x","light"]}]},"https://i.imgur.com/CE1FK1F.gif":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/CE1FK1F.gif","variants":[{"url":"https:\/\/i.imgur.com\/CE1FK1F.gif","traits":["1x","light"]}]},"https://i.imgur.com/4msnSRe.gif":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/4msnSRe.gif","variants":[{"url":"https:\/\/i.imgur.com\/4msnSRe.gif","traits":["1x","light"]}]},"https://i.imgur.com/smR4FRt.gif":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/smR4FRt.gif","variants":[{"url":"https:\/\/i.imgur.com\/smR4FRt.gif","traits":["1x","light"]}]},"https://i.imgur.com/AjhHOxd.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/AjhHOxd.png","variants":[{"url":"https:\/\/i.imgur.com\/AjhHOxd.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/dEYLhwy.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/dEYLhwy.png","variants":[{"url":"https:\/\/i.imgur.com\/dEYLhwy.png","traits":["1x","light"]}]},"https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/typography/":{"titleInlineContent":[{"type":"text","text":"Dynamic Type"}],"url":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/visual-design\/typography\/","type":"link","title":"Dynamic Type","identifier":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/visual-design\/typography\/"},"https://developer.apple.com/documentation/uikit/uiviewcontroller/1621446-loadviewifneeded":{"titleInlineContent":[{"type":"text","text":"loadviewifneeded()"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621446-loadviewifneeded","type":"link","title":"loadviewifneeded()","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621446-loadviewifneeded"},"https://i.imgur.com/v0hFYIf.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/v0hFYIf.png","variants":[{"url":"https:\/\/i.imgur.com\/v0hFYIf.png","traits":["1x","light"]}]},"https://developer.apple.com/design/human-interface-guidelines/ios/visual-design/adaptivity-and-layout/":{"titleInlineContent":[{"type":"text","text":"링크"}],"url":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/visual-design\/adaptivity-and-layout\/","type":"link","title":"링크","identifier":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/visual-design\/adaptivity-and-layout\/"},"https://i.imgur.com/Ln5xGmo.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/Ln5xGmo.png","variants":[{"url":"https:\/\/i.imgur.com\/Ln5xGmo.png","traits":["1x","light"]}]},"https://gmlwjd9405.github.io/2018/07/04/class-diagram.html":{"titleInlineContent":[{"type":"text","text":"class diagram"}],"url":"https:\/\/gmlwjd9405.github.io\/2018\/07\/04\/class-diagram.html","type":"link","title":"class diagram","identifier":"https:\/\/gmlwjd9405.github.io\/2018\/07\/04\/class-diagram.html"},"https://i.imgur.com/gxIT2hn.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/gxIT2hn.png","variants":[{"url":"https:\/\/i.imgur.com\/gxIT2hn.png","traits":["1x","light"]}]},"https://i.imgur.com/cxDxkwX.gif":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/cxDxkwX.gif","variants":[{"url":"https:\/\/i.imgur.com\/cxDxkwX.gif","traits":["1x","light"]}]},"https://github.com/MarcoEidinger/SwiftPlantUML":{"titleInlineContent":[{"type":"text","text":"참고한 swift UML 예제"}],"url":"https:\/\/github.com\/MarcoEidinger\/SwiftPlantUML","type":"link","title":"참고한 swift UML 예제","identifier":"https:\/\/github.com\/MarcoEidinger\/SwiftPlantUML"},"https://i.imgur.com/tlBQ8Am.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/tlBQ8Am.png","variants":[{"url":"https:\/\/i.imgur.com\/tlBQ8Am.png","traits":["1x","light"]}]},"https://i.imgur.com/jBdIYkK.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/jBdIYkK.png","variants":[{"url":"https:\/\/i.imgur.com\/jBdIYkK.png","traits":["1x","light"]}]},"https://i.imgur.com/3TB3CiV.gif":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/3TB3CiV.gif","variants":[{"url":"https:\/\/i.imgur.com\/3TB3CiV.gif","traits":["1x","light"]}]},"https://i.imgur.com/1w4TvUT.gif":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/1w4TvUT.gif","variants":[{"url":"https:\/\/i.imgur.com\/1w4TvUT.gif","traits":["1x","light"]}]},"https://i.imgur.com/luejd48.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/luejd48.png","variants":[{"url":"https:\/\/i.imgur.com\/luejd48.png","traits":["1x","light"]}]},"https://i.imgur.com/BQDWk8O.gif":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/BQDWk8O.gif","variants":[{"url":"https:\/\/i.imgur.com\/BQDWk8O.gif","traits":["1x","light"]}]},"https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/AutolayoutPG/Size-ClassSpecificLayout.html#//apple_ref/doc/uid/TP40010853-CH26-SW1":{"titleInlineContent":[{"type":"text","text":"Size Class"}],"url":"https:\/\/developer.apple.com\/library\/archive\/documentation\/UserExperience\/Conceptual\/AutolayoutPG\/Size-ClassSpecificLayout.html#\/\/apple_ref\/doc\/uid\/TP40010853-CH26-SW1","type":"link","title":"Size Class","identifier":"https:\/\/developer.apple.com\/library\/archive\/documentation\/UserExperience\/Conceptual\/AutolayoutPG\/Size-ClassSpecificLayout.html#\/\/apple_ref\/doc\/uid\/TP40010853-CH26-SW1"},"https://i.imgur.com/KGL4YuW.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/KGL4YuW.png","variants":[{"url":"https:\/\/i.imgur.com\/KGL4YuW.png","traits":["1x","light"]}]},"https://i.imgur.com/Mo1HvY1.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/Mo1HvY1.png","variants":[{"url":"https:\/\/i.imgur.com\/Mo1HvY1.png","traits":["1x","light"]}]},"https://i.imgur.com/O8T3VPE.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/O8T3VPE.png","variants":[{"url":"https:\/\/i.imgur.com\/O8T3VPE.png","traits":["1x","light"]}]},"https://i.imgur.com/F7HaWjY.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/F7HaWjY.png","variants":[{"url":"https:\/\/i.imgur.com\/F7HaWjY.png","traits":["1x","light"]}]},"https://i.imgur.com/8M0ntrG.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/8M0ntrG.png","variants":[{"url":"https:\/\/i.imgur.com\/8M0ntrG.png","traits":["1x","light"]}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211102_loadviewifneeded,_adjustsfontforcontentsizecategory,_adjustsfontsizetofitwidth,_uibutton,_navigationviewcontroller,_removearrangedsubview.json b/docs/data/documentation/arinote/211102_loadviewifneeded,_adjustsfontforcontentsizecategory,_adjustsfontsizetofitwidth,_uibutton,_navigationviewcontroller,_removearrangedsubview.json new file mode 100644 index 0000000..262734f --- /dev/null +++ b/docs/data/documentation/arinote/211102_loadviewifneeded,_adjustsfontforcontentsizecategory,_adjustsfontsizetofitwidth,_uibutton,_navigationviewcontroller,_removearrangedsubview.json @@ -0,0 +1 @@ +{"schemaVersion":{"patch":0,"major":0,"minor":3},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211102_loadviewifneeded,_adjustsfontforcontentsizecategory,_adjustsfontsizetofitwidth,_uibutton,_navigationviewcontroller,_removearrangedsubview"]}],"primaryContentSections":[{"content":[{"type":"heading","level":1,"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)"},{"inlineContent":[{"text":"11월 2일 (화)","type":"text"}],"type":"paragraph"},{"level":2,"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용"},{"inlineContent":[{"text":"오늘은 STEP 3를 구현하는 것에 대해서 시간을 많이 보냈다. 오후에는 의존모둠과 함께 클래스와 구조체에 대해서 의논하였고 끝나고난 이후에 제이티와 함께 PR과 README를 채워보았다.","type":"text"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"type":"heading","level":2,"text":"문제점 \/ 고민한 점","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"ViewController에 다음 View에 대한 프로퍼티에 접근되지가 않았던 문제를 해결하려면 어떻게 했었지?"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"button에 Dynamic Type을 적용시켜주려면?"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"button의 Label의 크기가 버튼의 너비에 맞게 fix해주려면 어떻게 해야할까?","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"NavigationViewController에 연결되어있는 ViewController를 불러오는 여러가지 방법"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Bundle에 대한 구체적인 개념이 궁금하다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Button을 코드로 구현하면 버튼을 클릭할 때 액션이 없다!"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Auto Layout 강의 예제 진행시 remove버튼을 클릭하면 뷰가 안사라지는 현상을 경험했다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"type":"heading","text":"해결방법","anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","level":2},{"items":[{"content":[{"inlineContent":[{"code":"loadViewIfNeeded()","type":"codeVoice"},{"type":"text","text":"를 이용하여 View를 미리 load해서 해당 뷰 프로퍼티에 접근하였다. 하지만 적절한 방법인지는 모르겠다. 이리저리 돌아댕기면서 캠퍼들은 어떻게 구현했나 의견을 구해보았는데 didSet을 이용하여 데이터를 전달해주는 방법도 있었다. 보통 뷰간에 데이터를 전달할 때는 직접 전달하기 보다는 값을 담을 프로퍼티를 미리 만들어두고 값을 전달한 다음 해당 화면에서 값을 대입하는 방식을 택한다고 한다. 데이터가 안전하게 전달될 수 있도록 해당 방법을 쓰는 것 같은데, 왜 직접 전달하는게 안좋은 걸까? 이 부분은 리뷰어분께 조언을 구하기로 했다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"버튼의 title Label은 Inspector에서 Automatically Adjusts Font를 설정할 수가 없었다. 찾아보았지만 코드로 구현하는 것이 최선이였다."}]},{"code":["orderStrawberryBananaJuiceButton.titleLabel?.adjustsFontForContentSizeCategory = true"],"syntax":"swift","type":"codeListing"}]},{"content":[{"inlineContent":[{"type":"text","text":"이것도 디스코드를 이리저리 돌아다니면서 nicho, 고사리 팀에서 힌트를 얻었던게 생각이 나서 해결해보았다."}],"type":"paragraph"},{"syntax":"swift","code":["orderStrawberryBananaJuiceButton.titleLabel?.adjustsFontSizeToFitWidth = true"],"type":"codeListing"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NavigationViewController에 속성값 중에 topViewController 라는 속성을 사용하여 접근할 수도 있고, viewControllers 배열에 접근해서 활용하는 방법, ViewController 속성값 중에 children이라는 속성도 활용할 수 있었다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"번들은 백팩이라고 할 수 있다. 이 가방엔 내가 원하는 무엇이든 담을 수 있는 특별한 주머니가 있고 이 주머니는 어디를 가던 장소에 딱 맞게 바뀌어서 나온다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"오늘도 제이티의 꿀팁… Button을 생성해줄때 이니셜라이저 Type에다가 .system이라는 설정값을 줘야 버튼을 누를때 클릭되는 액션[?]이 생긴다","type":"text"}]},{"syntax":"swift","type":"codeListing","code":["UIButton(type: .system)"]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"공식문서를 살펴보면 view를 지울 때 "},{"code":"isHidden = true","type":"codeVoice"},{"text":"를 설정해주거나 removeFromSuperview()를 호출하여 뷰를 명시적으로 제거하라고 나와있었다. 둘중 하나를 설정해주지 않는다면 아무리 remove버튼을 눌러도 view가 데이터상에서는 사라지긴 하지만[?] 화면상에서는 사라지지 않고 남아있었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"isHidden을 true로 주었을 때","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/KBd6H3s.gif"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"isHidden을 true로 주지 않았을 때"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/rlVjRDr.gif"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uilabel\/1620546-adjustsfontsizetofitwidth","type":"reference","isActive":true}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uicontentsizecategoryadjusting\/1771731-adjustsfontforcontentsizecategor"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/nshipster.co.kr\/bundles-and-packages\/","isActive":true}]}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uinavigationcontroller\/1621849-topviewcontroller"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621452-children","isActive":true,"type":"reference"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uibutton\/buttontype"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uistackview\/1616235-removearrangedsubview","isActive":true}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}],"kind":"content"}],"kind":"article","sections":[],"metadata":{"role":"article","title":"211102 loadViewIfNeeded, adjustsFontForContentSizeCategory, adjustsFontSizeToFitWidth, UIButton, NavigationViewController, removearrangedsubview","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article"},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211102_loadViewIfNeeded,_adjustsFontForContentSizeCategory,_adjustsFontSizeToFitWidth,_UIButton,_NavigationViewController,_removearrangedsubview"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"references":{"https://developer.apple.com/documentation/uikit/uinavigationcontroller/1621849-topviewcontroller":{"titleInlineContent":[{"text":"topviewcontroller","type":"text"}],"type":"link","title":"topviewcontroller","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uinavigationcontroller\/1621849-topviewcontroller","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uinavigationcontroller\/1621849-topviewcontroller"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://developer.apple.com/documentation/uikit/uilabel/1620546-adjustsfontsizetofitwidth":{"titleInlineContent":[{"text":"adjustsFontSizeToFitWidth","type":"text"}],"type":"link","title":"adjustsFontSizeToFitWidth","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uilabel\/1620546-adjustsfontsizetofitwidth","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uilabel\/1620546-adjustsfontsizetofitwidth"},"https://i.imgur.com/KBd6H3s.gif":{"type":"image","identifier":"https:\/\/i.imgur.com\/KBd6H3s.gif","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/KBd6H3s.gif"}],"alt":null},"https://developer.apple.com/documentation/uikit/uiviewcontroller/1621452-children":{"titleInlineContent":[{"text":"children","type":"text"}],"type":"link","title":"children","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621452-children","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621452-children"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://developer.apple.com/documentation/uikit/uistackview/1616235-removearrangedsubview":{"titleInlineContent":[{"text":"removearrangedsubview","type":"text"}],"type":"link","title":"removearrangedsubview","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uistackview\/1616235-removearrangedsubview","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uistackview\/1616235-removearrangedsubview"},"https://developer.apple.com/documentation/uikit/uicontentsizecategoryadjusting/1771731-adjustsfontforcontentsizecategor":{"titleInlineContent":[{"text":"adjustsFontForContentSizeCategory","type":"text"}],"type":"link","title":"adjustsFontForContentSizeCategory","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uicontentsizecategoryadjusting\/1771731-adjustsfontforcontentsizecategor","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uicontentsizecategoryadjusting\/1771731-adjustsfontforcontentsizecategor"},"https://i.imgur.com/rlVjRDr.gif":{"type":"image","identifier":"https:\/\/i.imgur.com\/rlVjRDr.gif","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/rlVjRDr.gif"}],"alt":null},"https://developer.apple.com/documentation/uikit/uibutton/buttontype":{"titleInlineContent":[{"text":"buttontype","type":"text"}],"type":"link","title":"buttontype","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uibutton\/buttontype","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uibutton\/buttontype"},"https://nshipster.co.kr/bundles-and-packages/":{"titleInlineContent":[{"text":"번들과 패키지","type":"text"}],"type":"link","title":"번들과 패키지","url":"https:\/\/nshipster.co.kr\/bundles-and-packages\/","identifier":"https:\/\/nshipster.co.kr\/bundles-and-packages\/"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211103_tdd,_unit_test,_tableview.json b/docs/data/documentation/arinote/211103_tdd,_unit_test,_tableview.json new file mode 100644 index 0000000..68199bd --- /dev/null +++ b/docs/data/documentation/arinote/211103_tdd,_unit_test,_tableview.json @@ -0,0 +1 @@ +{"sections":[],"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211103_TDD,_Unit_Test,_TableView","interfaceLanguage":"swift"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211103_tdd,_unit_test,_tableview"]}],"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"211103 TDD, Unit Test, TableView"},"schemaVersion":{"patch":0,"minor":3,"major":0},"primaryContentSections":[{"content":[{"level":1,"anchor":"TIL-Today-I-Learned","type":"heading","text":"TIL (Today I Learned)"},{"inlineContent":[{"type":"text","text":"11월 3일 (수)"}],"type":"paragraph"},{"text":"학습 내용","level":2,"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"inlineContent":[{"text":"오늘은 목요일 활동학습을 위해 Unit Test와 TDD를 예습을 진행하였다. 이후에 Auto Layout의 진도를 조금 나가보았다. TDD는 이전에 flutter를 독학할 때 당시에 강의로 조금 배워본 적이 있는데, 오래되서 다 까먹어버렸다. 기억을 더듬어가며 다시 배워보았다.","type":"text"}],"type":"paragraph"},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","type":"heading","text":"문제점 \/ 고민한 점","level":2},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"현업에서는 Unit Test를 필수로 사용할까..?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"TDD로 개발을 진행하려면 어떤 방식으로 하는거였지?","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Auto Layout 강의 중에 TableView 관련 클래스와 프로토콜이 나오는데 뭐가 뭔지 하나도 모르겠네…","type":"text"},{"type":"text","text":" "},{"identifier":"https:\/\/i.imgur.com\/d9lzJpt.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"TableView에서 터치시 셀이 커지고 작아지는 기능을 구현하는데 예상치 못한 nil이 발생하였다며 제대로 동작하지가 않는다."}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"type":"heading","text":"해결방법","level":2,"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"기업마다 다르겠지만 많은 기업에서 Unit Test를 적용하여 개발을 진행한다고 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Red > Green > refacotr를 기억하자. TDD Cycle을 통해서 좋은 코드를 도출해내어 코드를 작성한다. (테스트코드 작성 > 테스트를 통과하는 코드를 작성 > 재사용성과 의존성이 낮은 코드를 작성 > 많은 성공을 이루어낸 코드를 최종적으로 작성)","type":"text"}]}]},{"content":[{"type":"heading","text":"UITableViewDataSource","anchor":"UITableViewDataSource","level":1},{"inlineContent":[{"text":"테이블 뷰의 셀에 사용되는 데이터를 관리하기 위해 채택하는 프로토콜","type":"text"},{"type":"text","text":" "},{"type":"text","text":"테이블 뷰는 데이터를 보여주기만 하는 것이지 자체적으로 데이터를 관리할 수는 없다. 그래서 해당 프로토콜을 사용해야한다.Data source object는 테이블에서 데이터와 관련된 요청이 오면 응답하며 테이블의 데이터를 직접 관리하거나 앱의 다른 부분과 조정하여 해당 데이터를 관리한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"아무리 찾아봐도 어떤 부분이 잘못됬는지 못찾았다… 분명 강의대로 따라서 했는데 오류는 왜 발생하는 것일까? 쩝…"}]}]}]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uilistcontentconfiguration","isActive":true}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/views_and_controls\/table_views\/filling_a_table_with_data","isActive":true}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewcell","isActive":true,"type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewdatasource","type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/ko.wikipedia.org\/wiki\/%ED%85%8C%EC%8A%A4%ED%8A%B8_%EC%A3%BC%EB%8F%84_%EA%B0%9C%EB%B0%9C","isActive":true,"type":"reference"}]}]},{"content":[{"inlineContent":[{"isActive":true,"identifier":"https:\/\/yagom.net\/courses\/unit-test-%ec%9e%91%ec%84%b1%ed%95%98%ea%b8%b0\/","type":"reference"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}],"kind":"content"}],"references":{"https://i.imgur.com/d9lzJpt.png":{"type":"image","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/d9lzJpt.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/d9lzJpt.png"},"https://developer.apple.com/documentation/uikit/uitableviewdatasource":{"type":"link","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewdatasource","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewdatasource","title":"UITableViewDataSource","titleInlineContent":[{"type":"text","text":"UITableViewDataSource"}]},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://yagom.net/courses/unit-test-%ec%9e%91%ec%84%b1%ed%95%98%ea%b8%b0/":{"type":"link","url":"https:\/\/yagom.net\/courses\/unit-test-%ec%9e%91%ec%84%b1%ed%95%98%ea%b8%b0\/","identifier":"https:\/\/yagom.net\/courses\/unit-test-%ec%9e%91%ec%84%b1%ed%95%98%ea%b8%b0\/","title":"Unit Test 강의","titleInlineContent":[{"text":"Unit Test 강의","type":"text"}]},"https://ko.wikipedia.org/wiki/%ED%85%8C%EC%8A%A4%ED%8A%B8_%EC%A3%BC%EB%8F%84_%EA%B0%9C%EB%B0%9C":{"type":"link","url":"https:\/\/ko.wikipedia.org\/wiki\/%ED%85%8C%EC%8A%A4%ED%8A%B8_%EC%A3%BC%EB%8F%84_%EA%B0%9C%EB%B0%9C","identifier":"https:\/\/ko.wikipedia.org\/wiki\/%ED%85%8C%EC%8A%A4%ED%8A%B8_%EC%A3%BC%EB%8F%84_%EA%B0%9C%EB%B0%9C","title":"TDD","titleInlineContent":[{"type":"text","text":"TDD"}]},"https://developer.apple.com/documentation/uikit/views_and_controls/table_views/filling_a_table_with_data":{"type":"link","url":"https:\/\/developer.apple.com\/documentation\/uikit\/views_and_controls\/table_views\/filling_a_table_with_data","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/views_and_controls\/table_views\/filling_a_table_with_data","title":"Filling a Table with Data","titleInlineContent":[{"text":"Filling a Table with Data","type":"text"}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://developer.apple.com/documentation/uikit/uilistcontentconfiguration":{"type":"link","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uilistcontentconfiguration","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uilistcontentconfiguration","title":"UIListContentConfiguration","titleInlineContent":[{"type":"text","text":"UIListContentConfiguration"}]},"https://developer.apple.com/documentation/uikit/uitableviewcell":{"type":"link","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewcell","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewcell","title":"UITableViewCell","titleInlineContent":[{"text":"UITableViewCell","type":"text"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211104_tdd,_unit_test,_@discardableresult,_cell_style,_droplast(),_removelast(),_poplast().json b/docs/data/documentation/arinote/211104_tdd,_unit_test,_@discardableresult,_cell_style,_droplast(),_removelast(),_poplast().json new file mode 100644 index 0000000..e9e865a --- /dev/null +++ b/docs/data/documentation/arinote/211104_tdd,_unit_test,_@discardableresult,_cell_style,_droplast(),_removelast(),_poplast().json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211104_TDD,_Unit_Test,_@discardableResult,_Cell_Style,_dropLast(),_removeLast(),_popLast()"},"primaryContentSections":[{"content":[{"type":"heading","text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned","level":1},{"type":"paragraph","inlineContent":[{"type":"text","text":"11월 4일 (목)"}]},{"level":2,"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"inlineContent":[{"text":"오늘은 TDD와 Unit Test에 대한 활동학습을 진행하였다. Queue와 Stack구조를 간단히 알아가는 시간을 가지고 나서 Stack을 TDD 방식으로 구현해나가는 활동학습을 진행했다. 제리랑 같은 팀이 되서 구현하다가 여러가지 방법들로 구현해나가는 방법을 이야기하다가 문법 복습도 하게되었고, 다른 팀원들이 구현한 것도 나누면서 유익한 시간을 보냈다. 이후에는 제이티와 함께 피드백 온것을 고쳐보고 고민해보는 시간을 가지고 해산했다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"level":2,"type":"heading","text":"문제점 \/ 고민한 점","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"@discardableResult이 뭐야?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"TDD 방식은 왜 필요할까? 테스트로 어떤 이점을 가져올 수 있을까?","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"dropLast(), removeLast(), popLast() 다 같은일을 하는 것 같은데 차이점이 뭐였지?"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Cell의 style 종류에 대해서"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"TableView에서 터치시 셀이 커지고 작아지는 기능을 구현하는데 예상치 못한 nil이 발생하였다며 제대로 동작하지가 않는다.","type":"text"},{"text":" ","type":"text"},{"identifier":"https:\/\/i.imgur.com\/d9lzJpt.png","type":"image"}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","level":2,"text":"해결방법","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"@discardableResult","type":"codeVoice"},{"text":" 버릴 수 있는 결과라는 뜻이다. 결과를 return 하는데 이 결과가 필요 없는 경우도 있을 때, 그럴 때 warning을 보고싶지 않을 때 주로 사용한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테스트를 항상 통과하면서 코드를 작성해나가면 당연히 더 안전한 코드 작성할 수 있고, 테스트를 통과하는 코드를 작성하기 위해 재사용성과 의존성에 대해서 고민하여 의존성이 낮은 코드를 작성할 수 있다. 다만 개발 속도가 느리다는 단점이 있다. 모든 돌 다리를 두드려보고 건너려먼 당연히 안전함은 보장되겠지만 매번 그 과정이 있다면 당연히 진행속도는 저하될 것이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"dropLast()","type":"codeVoice"},{"type":"text","text":" 원본값에는 영향을 미치지 않고 SubString 또는 Slice 타입으로 반환해준다."},{"text":" ","type":"text"},{"type":"codeVoice","code":"removeLast()"},{"text":" 컬렉션 끝에 위치한 요소를 제거하고 반환한다.","type":"text"},{"type":"text","text":" "},{"type":"codeVoice","code":"popLast()"},{"type":"text","text":" 컬렉션 마지막 요소를 제거하고 반환하는데, 제거하지 못한다면 nil을 반환한다."}]},{"syntax":"swift","code":["func dropLast(_ k: Int) -> ArraySlice","@discardableResult mutating func removeLast() -> Element","mutating func popLast() -> Element?",""],"type":"codeListing"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Custom"},{"text":" 기본 스타일이며 개발자가 직접 UI 요소를 넣어서 사용할 수 있다","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/YRXv2M8.png","type":"image"}],"type":"paragraph"},{"inlineContent":[{"type":"codeVoice","code":"Basic"},{"type":"text","text":" TextView와 ImageView가 있는 심플한 스타일"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Qz9CViB.png"}]},{"type":"paragraph","inlineContent":[{"code":"Right Detail","type":"codeVoice"},{"type":"text","text":" Basic에서 Defail Text Label이 추가된 형태이며 Defail Text Label 부분이 오른쪽 정렬 되어있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/20PqndH.png","type":"image"}]},{"type":"paragraph","inlineContent":[{"code":"Left Detail","type":"codeVoice"},{"type":"text","text":" Image View를 포함하지 않은 형태이며 어느 한 기준선에서 Title Label은 오른쪽 정렬이, Detail Label은 왼쪽 정렬이 되어있다."}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/wJqPFtv.png","type":"image"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"subtitle"},{"text":" Image View를 포함하고 있으며 비교적 큰 사이즈의 Title Label과 바로 아래 작은 Detail Label을 가능 형태이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"제이티와 피드백 관련 의논을 마치고 혹시나해서 물어봤는데 바로 해결할 수 있는 방법을 찾아줬다… 고마워요…"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/w8E3Nue.png","type":"image"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 부분에서 tableView가 스토리보드와 연결이 안되어있어서 생기는 문제였다.. 다음부턴 같은 에러가 뜬다면 스토리보드와 잘 연결되어있는지 확인해보자!"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"text":"공부내용 정리","anchor":"%EA%B3%B5%EB%B6%80%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC","level":2,"type":"heading"},{"inlineContent":[{"text":"[TDD]","type":"text"}],"type":"paragraph"},{"style":"note","name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"테스트 주도 개발(Test-Driven Development TDD)은 매우 짧은 개발 사이클을 반복하는 소프트웨어 개발 프로세스 중 하나이다. 개발자는 먼저 요구사항을 검증하는 자동화 된 테스트 케이스를 작성한다. 그런 후에 그 테스트 케이스를 통과하기 위한 최소한의 코드를 생성한다. 마지막으로 작성한 코드를 표준에 맞도록 리팩토링한다. -위키백과","type":"text"}]}],"type":"aside"},{"inlineContent":[{"type":"text","text":"테스트 주도 개발을 말 그대로 개발을 하는데에 있어서 테스트가 주가 된다는 하나의 개발 방법론이다. 먼저 테스트를 하면서 코드를 작성하고 그 후에 본 코드를 구현하는 방식이다. 테스트를 거친 후에 코드를 작성한다면 추후에 신경 써줘야할 많은 부분들에 대해서 해결을 하면서 코드를 작성할 수 있겠다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/b5milZI.jpg","type":"image"}]},{"type":"paragraph","inlineContent":[{"text":"하지만 TDD를 언제나 고집하기에는 번거롭고 생산성이 떨어지는 측면도 있다. 또한 이미 프로그램이 만들어져있는 상황에서 TDD를 진행하는 것은 단연 무리가 있다. 이유는 테스트 코드를 염두해 두지 않고 무턱대고 코드부터 작성하게 되면 테스트가 불가능한 코드를 작성하게 될 가능성이 높아진다.","type":"text"}]},{"text":"TDD Cycle","type":"heading","anchor":"TDD-Cycle","level":1},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Cwp6VI8.jpg"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"TDD는 실패 - 성공 - 리팩토링의 짧은 주기를 반복하여 좋은 코드를 도출해내는 방식이다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Red 실패하는 테스트를 작성하는 구간","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Green 실패한 테스트를 통과하기 위해 최소한의 변경을 하여 테스트에 성공하는 구간"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Refactor 테스트의 성공을 유지하면서 코드를 더 나은 방향으로 개선해나가는 구간"}]}]}]},{"type":"heading","level":1,"anchor":"TDD%EC%9D%98-%EC%9E%A5%EB%8B%A8%EC%A0%90","text":"TDD의 장단점"},{"inlineContent":[{"type":"text","text":"TDD는 높은 퀄리티의 소프트웨어를 보장한다"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"에러나 버그가 발생하지 않는 코드를 작성할 수 있다"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"추가적인 요구사항이 있을 때 손쉽게 그 요구사항을 반영할 수 있다"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"유지보수에 용이하다"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"text":"테스트를 항상 통과하면서 코드를 작성해나가면 당연히 더 안전한 코드 작성을 할 수 있다.","type":"text"},{"type":"text","text":" "},{"text":"테스트를 통과하는 코드를 작성하기 위해 재사용성과 의존성에 대해서 고민하여 의존성이 낮은 코드를 작성할 수 있다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"그러나 치명적인 단점이 있는거 그건 개발 속도이다. 모든 돌 다리를 두드려보고 건너면 당연히 안전함은 보장되겠지만 매번 그 과정이 있다면 당연히 진행속도는 저하될 것이다."}]},{"inlineContent":[{"text":"유닛테스트는 다른 말로 단위 테스트라고 하는데, 하나의 함수, 메서드를 기준으로 독립적으로 진행되는 가장 작은 단위의 테스트이다. 즉, 메서드를 하나하나 테스트 하는 것과 같은 맥락이라고 볼 수 있다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"왜 필요한가?"},{"type":"text","text":" "},{"type":"text","text":"프로그램을 개발할 때 분명히 빌드를 하며 제대로 동작하는 것을 확인 하고 커밋을 할텐데? 오히려 테스트 코드를 작성하는 시간이 더 오래걸릴 것 같아 비효율적이라고 생각했다."},{"type":"text","text":" "},{"type":"text","text":"하지만 이미 많은 기업에서 유닛테스트를 적용하고 있다."}]},{"type":"orderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"각각의 모듈을 부분적으로 확인할 수 있어 어떤 묘듈에서 문제가 발생하는지 빠른 확인이 가능","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"전체 프로그램을 빌드하는 대신 유닛 단위로 빌드해 확인하므로 시간 절약"}]}]}]},{"type":"paragraph","inlineContent":[{"text":"F.I,R.S.T 단위 테스트 원칙","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Fast 유닛 테스트는 빨라야 한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Isolated 다른 테스트에 종속적인 테스트는 절대로 작성하지 않는다"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Repeatable 테스트는 실행할 때마다 같은 결과를 만들어야 한다","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Self-validation 테스트는 스스로 결과물이 옳은지 그른지 판단할 수 있어야한다. 특정 상태를 수동으로 미리 만들어야 동작하는 테스트 등은 작성하지 않는다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Timely 유닛 테스트는 프로덕션 코드가 테스트를 성공하기 직전에 구성되어야 한다. 테스트 주도 개발(TDD) 방법론에 적합한 원칙이지만 실제로 적용되지 않는 경우도 있다.","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"text":"테스트가 이루어 지는 방식","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"func testArraySorting() {","type":"text"},{"text":" ","type":"text"},{"text":"let input = [1, 7, 6, 3, 10]","type":"text"},{"type":"text","text":" "},{"type":"text","text":"let expectation = [1, 3, 6, 7, 10]"}]},{"type":"codeListing","code":["let result = input.sorted()","","XCTAssertEqual(result, expectation)"],"syntax":null},{"inlineContent":[{"type":"text","text":"}"}],"type":"paragraph"},{"inlineContent":[{"text":"예상값과 결괏값을 비교하는 식으로 진행된다.","type":"text"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"테스트가 가능한 코드란?"}],"type":"paragraph"},{"level":2,"type":"heading","text":"XCTest","anchor":"XCTest"},{"inlineContent":[{"type":"text","text":"유닛 테스트, 퍼포먼스 테스트, UI 테스트를 만들고 실행하는 프레임워크다."}],"type":"paragraph"},{"level":2,"type":"heading","text":"XCTestCase","anchor":"XCTestCase"},{"type":"paragraph","inlineContent":[{"type":"text","text":"추상 클래스인 XCTest의 하위 클래스로, 테스트를 작성하기 위해 상속해야 하는 가장 기본적인 클래스이다. XCTest는 테스트를 위한 프레임워크의 이름이기도 하고, 테스트에서 가장 기본이 되는 추상 클래스의 이름이기도 하다."},{"type":"text","text":" "},{"text":"해당 클래스를 상속받은 클래스에서는 test에서 사용되는 다양한 프로퍼티와 메서드를 사용할 수 있다.","type":"text"}]},{"text":"setUpWithError()","level":2,"type":"heading","anchor":"setUpWithError"},{"type":"paragraph","inlineContent":[{"type":"text","text":"각각의 test case가 실행되기 전마다 호출되어 각 테스트가 모두 같은 상태와 조건에서 실행될 수 있도록 만들어줄 수 있는 메서드다."}]},{"level":2,"anchor":"tearDownWithError","type":"heading","text":"tearDownWithError()"},{"inlineContent":[{"text":"각각의 test 실행이 끝난 후마다 호출되는 메서드. 보통 setUpWithError()에서 설정한 값들을 해제할 때 사용된다.","type":"text"}],"type":"paragraph"},{"type":"heading","anchor":"%ED%98%B8%EC%B6%9C-%EC%88%9C%EC%84%9C","text":"호출 순서","level":2},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/NfLv9ZU.png"}],"type":"paragraph"},{"text":"testExample()","anchor":"testExample","type":"heading","level":2},{"type":"paragraph","inlineContent":[{"type":"text","text":"test로 시작하는 메서드들은 작성해야 할 test case가 되는 메서드다. 테스트할 내용을 메서드로 작성해 볼 수 있다. 메서드 네이밍은 무조건 test로 시작되어야 한다."}]},{"type":"heading","text":"testPerformanceExample()","anchor":"testPerformanceExample","level":2},{"type":"paragraph","inlineContent":[{"type":"text","text":"성능을 테스트해보기 위한 메서드다. XCTestCase의 measure(block:)라는 메서드를 통해 성능을 측정하게 된다."}]},{"anchor":"%ED%85%8C%EC%8A%A4%ED%8A%B8-%ED%8F%AC%EB%A7%B7","type":"heading","level":1,"text":"테스트 포맷"},{"inlineContent":[{"type":"text","text":"테스트 포맷은 given - when - then 구조로 작성하는 것이 좋다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Given : 필요한 vlaue들을 셋팅","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"When 테스트 코드 실행","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Then 결과 확인"}]}]}]},{"type":"heading","level":1,"anchor":"Code-Coverage-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0","text":"Code Coverage 확인하기"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/esc6RBo.png","type":"image"},{"type":"text","text":" "},{"type":"image","identifier":"https:\/\/i.imgur.com\/46gsLiZ.png"},{"text":" ","type":"text"},{"type":"image","identifier":"https:\/\/i.imgur.com\/uViiobs.png"},{"text":" ","type":"text"},{"type":"image","identifier":"https:\/\/i.imgur.com\/XFdjM4B.gif"}]},{"level":1,"text":"Test Double","anchor":"Test-Double","type":"heading"},{"inlineContent":[{"text":"테스트를 진행하기 어려운 경우 이를 대신하여 테스트를 진행할 수 있도록 만들어주는 객체를 말한다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"테스트 대상 코드를 격리한다","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"테스트 속도를 개선한다"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"예측 불가능한 실행 요소를 제거한다"}]}]},{"content":[{"inlineContent":[{"text":"특수한 상황을 시뮬레이션한다","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"감춰진 정보를 얻어낸다"}],"type":"paragraph"}]}],"type":"unorderedList"},{"level":2,"anchor":"%EC%A2%85%EB%A5%98","text":"종류","type":"heading"},{"inlineContent":[{"type":"text","text":"테스트 더블에는 Dummy, Stub, Fake, Spy, Mock 등이 있다. 테스트 더블마다 역할이 다르지만 명확한 기준으로 구분해서 사용하는 것은 아니다."}],"type":"paragraph"},{"anchor":"Dummy-%EB%AA%A8%EC%A1%B0%EC%9D%98-%EA%B0%80%EC%A7%9C%EC%9D%98","text":"Dummy (모조의 ,가짜의)","level":3,"type":"heading"},{"inlineContent":[{"text":"가장 기본적인 테스트 더블이다. 어떤 기능이 구현되어 있지 않은 단지 인스천화된 객체로 사용되기 때문에 Dummy의 메서드는 정상적으로 동작하지 않는다. 객체를 전달하기 위한 목적으로 주로 사용된다.","type":"text"}],"type":"paragraph"},{"type":"heading","level":3,"text":"Stub (쓰다 남은 물건의 토막, 남은 부분)","anchor":"Stub-%EC%93%B0%EB%8B%A4-%EB%82%A8%EC%9D%80-%EB%AC%BC%EA%B1%B4%EC%9D%98-%ED%86%A0%EB%A7%89-%EB%82%A8%EC%9D%80-%EB%B6%80%EB%B6%84"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Dummy가 실제로 동작하는 것처럼 만들어 실제 코드를 대신해서 동작해주는 객체다. 테스트가 곤란한 부분의 객체를 도려내어 그 역할을 최소한으로 대신해 줄 만큼만 간단하게 구현되어 있다."}]},{"anchor":"Fake","level":3,"text":"Fake","type":"heading"},{"type":"paragraph","inlineContent":[{"text":"Stub보다 구체적으로 동작해서 실제 로직처럼 보이지만 실제 앱의 동작에서는 적합하지 않은 객체를 말한다. 로직 자체는 실제 앱의 코드와 비슷하지만 그 동작을 단순화하여 구현한 객체를 Fake 객체라고 한다.","type":"text"}]},{"anchor":"Spy","text":"Spy","type":"heading","level":3},{"type":"paragraph","inlineContent":[{"type":"text","text":"Stub의 역할을 가지면서 호출된 내용에 대한 방법 혹은 과정 등 약간의 정보를 기록하는 객체다. 예를 들어 호출되었는지 몇 번 호출되었는지 등에 대한 정보를 기록할 수 있다."}]},{"type":"heading","text":"Mock","anchor":"Mock","level":3},{"type":"paragraph","inlineContent":[{"text":"실제 객체와 가장 비슷하게 구현된 수준의 객체라고 할 수 있다. Stub이 상태 기반 테스트(State Base Test)라면 Mock은 행위 기반 테스트(Behavior Base Test)라고 이야기하기도 한다. 여기서 상태 기반 테스트는 메서드를 호출하고 그 결괏값과 예상 값을 비교하는 식으로 동작하는 테스트를 말하고, 행위 기반 테스트는 예상되는 행위들에 대한 시나리오를 만들어 놓고 시나리오대로 동작했는지에 대한 여부를 확인하는 것이다.","type":"text"}]},{"type":"heading","text":"의존성 주입 (Dependency Injection)","anchor":"%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85-Dependency-Injection","level":1},{"type":"paragraph","inlineContent":[{"type":"text","text":"하나의 객체가 다른 객체의 의존성을 제공하는 기술로 줄여서 DI(Dependency Injection)라고 부르기도 한다."}]},{"type":"heading","anchor":"%EC%9D%98%EC%A1%B4%EC%84%B1%EC%9D%B4%EB%9E%80","text":"의존성이란?","level":2},{"inlineContent":[{"text":"어떤 객체가 내부에서 생성하여 가지고 있는 객체를 의존성이라고 한다.","type":"text"}],"type":"paragraph"},{"level":2,"text":"의존성 주입이란?","anchor":"%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85%EC%9D%B4%EB%9E%80","type":"heading"},{"type":"paragraph","inlineContent":[{"text":"말 그대로 의존성을 주입시킨다는 뜻이다. 내부에서 초기화가 이루어지는 것이 아니라 외부에서 객체를 생성하여 내부에 주입해주는 것을 뜻한다.","type":"text"}]},{"level":2,"type":"heading","anchor":"%EC%99%9C-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C","text":"왜 의존성 주입을 사용할까?"},{"type":"paragraph","inlineContent":[{"type":"text","text":"의존성 주입을 사용하는 이유는 객체간의 결합도를 낮추기 위해서다. 객체 간의 결합도가 낮으면 리팩토링이 쉽고 테스트 코드 작성이 쉬워진다는 장점이 있다."}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/docs.swift.org\/swift-book\/ReferenceManual\/Attributes.html","isActive":true,"type":"reference"}]}]},{"content":[{"inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewcell\/cellstyle"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/views_and_controls\/table_views\/configuring_the_cells_for_your_table","isActive":true,"type":"reference"}]}]}],"type":"unorderedList"}]}]}],"kind":"content"}],"variants":[{"paths":["\/documentation\/arinote\/211104_tdd,_unit_test,_@discardableresult,_cell_style,_droplast(),_removelast(),_poplast()"],"traits":[{"interfaceLanguage":"swift"}]}],"schemaVersion":{"major":0,"minor":3,"patch":0},"kind":"article","sections":[],"metadata":{"title":"211104 TDD, Unit Test, @discardableResult, Cell Style, dropLast(), removeLast(), popLast()","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article"},"references":{"https://i.imgur.com/w8E3Nue.png":{"identifier":"https:\/\/i.imgur.com\/w8E3Nue.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/w8E3Nue.png","traits":["1x","light"]}],"type":"image"},"https://i.imgur.com/wJqPFtv.png":{"identifier":"https:\/\/i.imgur.com\/wJqPFtv.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/wJqPFtv.png","traits":["1x","light"]}],"type":"image"},"https://i.imgur.com/Qz9CViB.png":{"identifier":"https:\/\/i.imgur.com\/Qz9CViB.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/Qz9CViB.png","traits":["1x","light"]}],"type":"image"},"https://i.imgur.com/XFdjM4B.gif":{"identifier":"https:\/\/i.imgur.com\/XFdjM4B.gif","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/XFdjM4B.gif","traits":["1x","light"]}],"type":"image"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/b5milZI.jpg":{"identifier":"https:\/\/i.imgur.com\/b5milZI.jpg","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/b5milZI.jpg","traits":["1x","light"]}],"type":"image"},"https://i.imgur.com/46gsLiZ.png":{"identifier":"https:\/\/i.imgur.com\/46gsLiZ.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/46gsLiZ.png","traits":["1x","light"]}],"type":"image"},"https://developer.apple.com/documentation/uikit/views_and_controls/table_views/configuring_the_cells_for_your_table":{"title":"Configuring the Cells for Your Table","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/views_and_controls\/table_views\/configuring_the_cells_for_your_table","titleInlineContent":[{"text":"Configuring the Cells for Your Table","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/views_and_controls\/table_views\/configuring_the_cells_for_your_table","type":"link"},"https://developer.apple.com/documentation/uikit/uitableviewcell/cellstyle":{"title":"cellstyle","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewcell\/cellstyle","titleInlineContent":[{"text":"cellstyle","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewcell\/cellstyle","type":"link"},"https://i.imgur.com/d9lzJpt.png":{"identifier":"https:\/\/i.imgur.com\/d9lzJpt.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/d9lzJpt.png","traits":["1x","light"]}],"type":"image"},"https://i.imgur.com/esc6RBo.png":{"identifier":"https:\/\/i.imgur.com\/esc6RBo.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/esc6RBo.png","traits":["1x","light"]}],"type":"image"},"https://i.imgur.com/Cwp6VI8.jpg":{"identifier":"https:\/\/i.imgur.com\/Cwp6VI8.jpg","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/Cwp6VI8.jpg","traits":["1x","light"]}],"type":"image"},"https://i.imgur.com/YRXv2M8.png":{"identifier":"https:\/\/i.imgur.com\/YRXv2M8.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/YRXv2M8.png","traits":["1x","light"]}],"type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/20PqndH.png":{"identifier":"https:\/\/i.imgur.com\/20PqndH.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/20PqndH.png","traits":["1x","light"]}],"type":"image"},"https://i.imgur.com/uViiobs.png":{"identifier":"https:\/\/i.imgur.com\/uViiobs.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/uViiobs.png","traits":["1x","light"]}],"type":"image"},"https://i.imgur.com/NfLv9ZU.png":{"identifier":"https:\/\/i.imgur.com\/NfLv9ZU.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/NfLv9ZU.png","traits":["1x","light"]}],"type":"image"},"https://docs.swift.org/swift-book/ReferenceManual/Attributes.html":{"title":"Attributes","identifier":"https:\/\/docs.swift.org\/swift-book\/ReferenceManual\/Attributes.html","titleInlineContent":[{"text":"Attributes","type":"text"}],"url":"https:\/\/docs.swift.org\/swift-book\/ReferenceManual\/Attributes.html","type":"link"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211105_outlet_collection,_extension,_---_--_--,_---_--.json b/docs/data/documentation/arinote/211105_outlet_collection,_extension,_---_--_--,_---_--.json new file mode 100644 index 0000000..e19d8cd --- /dev/null +++ b/docs/data/documentation/arinote/211105_outlet_collection,_extension,_---_--_--,_---_--.json @@ -0,0 +1 @@ +{"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211105_Outlet_Collection,_extension,_---_--_--,_---_--","interfaceLanguage":"swift"},"primaryContentSections":[{"kind":"content","content":[{"type":"heading","anchor":"TIL-Today-I-Learned","level":1,"text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"text":"11월 5일 (금)","type":"text"}]},{"level":2,"text":"학습 내용","type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"paragraph","inlineContent":[{"type":"text","text":"오늘은 오전부터 제이티와 함께 흰에게 코드리뷰를 받는 시간을 가지고, 피드백을 반영하여 리팩토링을 하는 것으로 시간을 보냈다. 이후 느낀점들이 많아서 기록하고 잊을때 쯤 다시 보면서 리마인드하는 시간을 가져야겠다. 이후 README를 다듬으며 시간을 보냈다."}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"heading","level":2,"anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","text":"문제점 \/ 고민한 점"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"흰에게 코드를 설명하는 과정에서 제이티와 나의 의견이 다르다는 것을 깨달았다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"디자인 패턴이나 코드나 무엇이든 사용을 할때 정확한 근거와 이유를 가지고 사용해야겠다는 생각이 들었다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"프로젝트 STEP 진행에 있어서 중요한 것이 뭘까?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"배운 것을 무조건 프로젝트에 적용해보는게 좋은 걸까?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UILabel들을 배열에 담는 좋은 방법이 있을까?","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"가끔 UIButton 같은 내부 속성들을 스토리보드에서 다룰 수 없는 경우가 있는데, 이럴땐 코드로 구현해야한다. 근데 좀더 간결화할 순 없을까?","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"text":"해결방법","type":"heading","level":2,"anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"코드 리뷰를 하며 깨달았던 부분을 통해 프로젝트 코드 방향성에 대해서 다시한번 생각해보며 제이티와 다시 이야기를 나누었다. 프로젝트 진행함에 있어서 팀원과의 코드 방향성에 대해서 정확한 의사소통이 필요하다고 느꼈던 시간이였다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"배웠으니 사용해보면 좋겠다"},{"text":" 라는 이유로 싱글톤을 적용해보았는데, 그건 아주 잘못된 생각이였다. 또한 나름의 이유가 있어서 싱글톤을 적용해보면 좋겠다고 생각이 들었었는데, 다시 생각해보니 싱글톤이 없어도 충분히 구현할 수 있는 부분이였다는 걸 깨달았다. 그래도 싱글톤을 직접 겪으면서 장단점을 잘 파악할 수 있어서 뜻 깊었던 시간이였다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"흰이 리뷰를 해주면서 깨달음을 주었다. STEP 진행에 있어서 중요한 것은 스텝을 진행하면서 점점 ","type":"text"},{"type":"strong","inlineContent":[{"text":"코드를 개선하고 나아가는 방향","type":"text"}]},{"text":"으로 진행해야 된다는 것이다. 따라서 이전 스텝의 요구사항을 현재 진행하고 있는 스텝에서 신경 쓸 필요는 없는 것 같다는 생각이 들었다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"배운 것을 프로젝트에 적용해보는 것은 경험상 아주 좋은 것 같다. 특히나 배우는 입장에서는 말이다. 그러나 사용할 때 "},{"type":"codeVoice","code":"정확한 근거"},{"type":"text","text":"와 "},{"type":"codeVoice","code":"이유"},{"text":"를 토대로 사용을 해야할 것 같다. 막연히 ","type":"text"},{"type":"codeVoice","code":"배워보았으니 써볼까?"},{"text":" 라는 안일한 생각으로 사용하는 것은 이유없이 쓰는 것이나 마찬가지다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"디스코드 돌아다니다가 캠퍼 ","type":"text"},{"type":"codeVoice","code":"namu"},{"type":"text","text":"가 준 꿀팁이다. 프로젝트 진행시 배열로 뭔가 손쉽게 만들 수 있을 것 같은데 방법을 몰랐는데, 마침 나무가 엘리네 팀을 도와주면서 주었던 팁이다. (주섬주섬…)"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/6cqk3pd.png"}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/JUh5XXf.png","type":"image"}]},{"type":"paragraph","inlineContent":[{"text":"Outlet Collection을 사용하여 View를 배열로 만들 수 있었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"흰에게 조언을 구해서 알려주셨던 꿀팁인데 보통 현업에서는 UIButton에 extension해서 원하는 기능을 추가하고 적용하는 방식으로 작성한다고 한다. 이번 프로젝트를 개선해보면서 적용시켜보았다. 그랬더니 어느정도 코드를 간결화해줄 수 있었다.","type":"text"}]}]}]},{"code":["\/\/ 개선하기 전 코드","func setUpbuttonLabelFontAttributes() {"," orderStrawberryBananaJuiceButton.titleLabel?.adjustsFontForContentSizeCategory = true"," orderMangoKiwiJuiceButton.titleLabel?.adjustsFontForContentSizeCategory = true"," orderStrawberryJuiceButton.titleLabel?.adjustsFontForContentSizeCategory = true"," orderBananaJuiceButton.titleLabel?.adjustsFontForContentSizeCategory = true"," orderPineappleJuiceButton.titleLabel?.adjustsFontForContentSizeCategory = true"," orderKiwiJuiceButton.titleLabel?.adjustsFontForContentSizeCategory = true"," orderMangoJuiceButton.titleLabel?.adjustsFontForContentSizeCategory = true",""," orderStrawberryBananaJuiceButton.titleLabel?.adjustsFontSizeToFitWidth = true"," orderMangoKiwiJuiceButton.titleLabel?.adjustsFontSizeToFitWidth = true"," orderStrawberryJuiceButton.titleLabel?.adjustsFontSizeToFitWidth = true"," orderBananaJuiceButton.titleLabel?.adjustsFontSizeToFitWidth = true"," orderPineappleJuiceButton.titleLabel?.adjustsFontSizeToFitWidth = true"," orderKiwiJuiceButton.titleLabel?.adjustsFontSizeToFitWidth = true"," orderMangoJuiceButton.titleLabel?.adjustsFontSizeToFitWidth = true","}","","\/\/ 개선 후 코드","extension UIButton {"," func setUpTitleLabelFontAttributes() {"," titleLabel?.adjustsFontForContentSizeCategory = true"," titleLabel?.adjustsFontSizeToFitWidth = true"," }","}","","func setUpbuttonLabelFontAttributes() {"," orderStrawberryBananaJuiceButton.setUpTitleLabelFontAttributes()"," orderMangoKiwiJuiceButton.setUpTitleLabelFontAttributes()"," orderStrawberryJuiceButton.setUpTitleLabelFontAttributes()"," orderBananaJuiceButton.setUpTitleLabelFontAttributes()"," orderPineappleJuiceButton.setUpTitleLabelFontAttributes()"," orderKiwiJuiceButton.setUpTitleLabelFontAttributes()"," orderMangoJuiceButton.setUpTitleLabelFontAttributes()"," }"],"type":"codeListing","syntax":"swift"}]}],"schemaVersion":{"minor":3,"patch":0,"major":0},"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"211105 Outlet Collection, extension, 험난한 협업 과정, 이유와 근거"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211105_outlet_collection,_extension,_---_--_--,_---_--"]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"sections":[],"references":{"https://i.imgur.com/JUh5XXf.png":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/JUh5XXf.png","variants":[{"url":"https:\/\/i.imgur.com\/JUh5XXf.png","traits":["1x","light"]}]},"https://i.imgur.com/6cqk3pd.png":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/6cqk3pd.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/6cqk3pd.png"}]},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211108_delegate,_weak,_arc,_element,_queue,_stack,_linkelist.json b/docs/data/documentation/arinote/211108_delegate,_weak,_arc,_element,_queue,_stack,_linkelist.json new file mode 100644 index 0000000..d8c1a9b --- /dev/null +++ b/docs/data/documentation/arinote/211108_delegate,_weak,_arc,_element,_queue,_stack,_linkelist.json @@ -0,0 +1 @@ +{"schemaVersion":{"minor":3,"major":0,"patch":0},"metadata":{"roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"role":"article","title":"211108 Delegate, weak, ARC, Element, Queue, Stack, LinkeList"},"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"sections":[],"variants":[{"paths":["\/documentation\/arinote\/211108_delegate,_weak,_arc,_element,_queue,_stack,_linkelist"],"traits":[{"interfaceLanguage":"swift"}]}],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211108_Delegate,_weak,_ARC,_Element,_Queue,_Stack,_LinkeList"},"primaryContentSections":[{"content":[{"level":1,"text":"TIL (Today I Learned)","type":"heading","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"type":"text","text":"11월 8일 (월)"}]},{"level":2,"text":"학습 내용","type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"inlineContent":[{"type":"text","text":"오늘은 프로토콜에 대해서 활동학습을 진행하였다. Delegate 패턴이 잘 이해가 안갔었는데, 학습을 통해서 조금이나마 이해해보려고 노력했다… 이후에는 새로운 프로젝트인 계산기 프로젝트 STEP 1을 진행하였다. 진행하기 앞서 고사리가 ARC관련에 대해서 의문점을 가지고 있어서 따라다니며 해결하는 과정을 같이 겪었는데, ARC는 언제나 어렵다.."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","level":2,"type":"heading","text":"문제점 \/ 고민한 점"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Delegate 패턴이 잘 이해가 가지않았다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"열거형 타입은 어떨 때 사용하면 좋을까?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Delegate 관련 공식문서를 볼 때 Ask와 Tall의 차이?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"IBOutlet에 weak가 붙는 이유"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Element의 용도가 뭘까? T랑 다른점은?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Queue를 배열로 구현해도 괜찮을까?"}]}]}]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"level":2,"text":"해결방법","type":"heading","anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Delgate 패턴에 대해서 이해해보려고 여러가지의 글들을 찾아보면서 정리해보았다."}],"type":"paragraph"},{"text":"Delegation 디자인 패턴","type":"heading","level":1,"anchor":"Delegation-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4"},{"type":"paragraph","inlineContent":[{"text":"어떤 상황이 발생했을 때 어떤 객체에게 응답을 준다.","type":"text"},{"text":" ","type":"text"},{"text":"약간 리모콘을 발동시킨다는 느낌?","type":"text"}]},{"anchor":"%EC%84%A0%EC%96%B8%EB%B6%80-%EC%B2%98%EB%A6%AC%ED%95%98%EB%9D%BC%EA%B3%A0-%EC%8B%9C%ED%82%A4%EB%8A%94-%EA%B0%9D%EC%B2%B4-%EC%A3%BC%EB%A1%9C-%EC%9D%B4%EB%B2%A4%ED%8A%B8%EB%A5%BC-%EB%B0%9B%EB%8A%94-%EB%B7%B0%EB%93%A4","type":"heading","text":"선언부 (처리하라고 시키는 객체, 주로 이벤트를 받는 뷰들)","level":3},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"리모콘호출"}]},{"type":"text","text":" 응답을 받아서 일을 처리할 클래스에 프로토콜 타입 프로퍼티를 옵셔널로 선언한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"리모콘작동","type":"text"}],"type":"strong"},{"text":" 어느 시점에서 실행할지 정했던 부분에 프로토콜의 메서드를 호출해주면 된다.","type":"text"}]}]}]},{"anchor":"%EA%B5%AC%ED%98%84%EB%B6%80-%EB%8C%80%EC%8B%A0-%EC%B2%98%EB%A6%AC%ED%95%B4%EC%A4%84-%EA%B0%9D%EC%B2%B4--ViewController","text":"구현부 (대신 처리해줄 객체 , ViewController)","level":3,"type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로토콜을 생성한다."}]}]},{"content":[{"inlineContent":[{"inlineContent":[{"text":"리모콘설계","type":"text"}],"type":"strong"},{"type":"text","text":" 첫번째 뷰에 delegate를 채택하고 프로토콜을 준수한다 (메서드 구현 등…)"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"리모콘전달","type":"text"}],"type":"strong"},{"text":" 프로토콜을 채택하고 준수한 첫번째 뷰에서 리모콘을 실행할 다음 뷰로 넘어갈때 자신을 넘겨준다
(예시: nextView.delegate = self)","type":"text"}]}]}]},{"level":3,"type":"heading","text":"언제 사용할까?","anchor":"%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"하나의 객체가 해야하는일이 여러가지일 때"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"수신받는 객체가 많을 때, 콜백 블럭을 받기 위한 목적이 분명할 때","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"내부의 블록을 호출시키는 코드를 읽고 다시 돌아와서 추적할 일이 없는 경우","type":"text"}]}]}]}]}],"type":"unorderedList"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"열거형은 변하지 않을 값들을 묶을 때 사용하면 좋다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"(예시: 월,화,수,목,금,토,일)","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Ask 는 비서의 응답을 듣는다. tell은 비서의 응답을 듣지 않는다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"뷰끼리 순환참조가 가능해서 label은 weak 키워드로 만들어주는 것이 적절하다. 뷰 종료시 참조 카운팅(메모리 누수)을 피하기 위해서 weak 키워드를 쓴다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Element는 요소라는 뜻이다. 공식문서를 참고하고 stackoverflow를 참고하니 T와 Element 차이는 크게 없고 이름을 줄지, T로 명시할지는 사용자의 몫이라고 되어있다. 이번 프로젝트에서 나는 Element로 명시해주기로 하였다. 그게 좀더 코드를 보는데 있어서 직관적인 것 같다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"시간복잡도에 대해서 공부해보니 Queue를 배열로 구현할 때 dequeue 작업이 시간복잡도 O(n)이 걸려서 비효율적이라고 한다. 따라서 삽입\/삭제가 O(1)이 소요되는 LinkedList나 DoubleStack을 활용하여 구현해보았다.","type":"text"}],"type":"paragraph"},{"anchor":"%EC%8B%9C%EA%B0%84%EB%B3%B5%EC%9E%A1%EB%8F%84","type":"heading","level":1,"text":"시간복잡도"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"O(1) – 상수 시간"}]},{"type":"text","text":" : 문제를 해결하는데 오직 한 단계만 처리함."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"O(log n) – 로그 시간"}]},{"text":" : 문제를 해결하는데 필요한 단계들이 연산마다 특정 요인에 의해 줄어듬.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"O(n) – 직선적 시간"}]},{"text":" : 문제를 해결하기 위한 단계의 수와 입력값 n이 1:1 관계를 가짐.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"O(n log n)"}]},{"type":"text","text":" : 문제를 해결하기 위한 단계의 수가 N*(log2N) 번만큼의 수행시간을 가진다. (선형로그형)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"O(n^2)"}]},{"type":"text","text":" – "},{"inlineContent":[{"type":"text","text":"2차 시간"}],"type":"strong"},{"type":"text","text":" : 문제를 해결하기 위한 단계의 수는 입력값 n의 제곱."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"O(C^n)"}]},{"text":" – ","type":"text"},{"inlineContent":[{"type":"text","text":"지수 시간"}],"type":"strong"},{"text":" : 문제를 해결하기 위한 단계의 수는 주어진 상수값 C 의 n 제곱.","type":"text"}]}]}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"identifier":"https:\/\/stackoverflow.com\/questions\/55542757\/element-vs-generic-type-t","type":"reference","isActive":true}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/sequence\/2908099-element","type":"reference","isActive":true}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"references":{"https://stackoverflow.com/questions/55542757/element-vs-generic-type-t":{"titleInlineContent":[{"text":"Element vs. Generic Type ","type":"text"}],"url":"https:\/\/stackoverflow.com\/questions\/55542757\/element-vs-generic-type-t","type":"link","identifier":"https:\/\/stackoverflow.com\/questions\/55542757\/element-vs-generic-type-t","title":"Element vs. Generic Type "},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://developer.apple.com/documentation/swift/sequence/2908099-element":{"titleInlineContent":[{"text":"Element","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/swift\/sequence\/2908099-element","identifier":"https:\/\/developer.apple.com\/documentation\/swift\/sequence\/2908099-element","title":"Element","type":"link"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211109_removeall,_attribute,_commit,_numeric,_floatingpoint,_nan,_where,_sourcetree.json b/docs/data/documentation/arinote/211109_removeall,_attribute,_commit,_numeric,_floatingpoint,_nan,_where,_sourcetree.json new file mode 100644 index 0000000..722d5a4 --- /dev/null +++ b/docs/data/documentation/arinote/211109_removeall,_attribute,_commit,_numeric,_floatingpoint,_nan,_where,_sourcetree.json @@ -0,0 +1 @@ +{"metadata":{"modules":[{"name":"Ari's Notes"}],"title":"211109 removeAll, attribute, commit, Numeric, FloatingPoint, NaN, where, SourceTree","roleHeading":"Article","role":"article"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","level":1,"type":"heading","text":"TIL (Today I Learned)"},{"inlineContent":[{"type":"text","text":"11월 9일 (화)"}],"type":"paragraph"},{"text":"학습 내용","level":2,"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"inlineContent":[{"type":"text","text":"오늘은 첫 데일리 스크럼을 가지고나서 STEP 1 리드미와 PR 작성을 하였다. 이후에는 STEP 2를 어떤식으로 구현해볼지 시뮬레이션을 해보았다. 시뮬레이션을 해보면서 궁금한 지식들을 학습하고, 이후 프로젝트 피드백이 와서 어떤 부분을 배우고, 복습해야할지 정리해보았다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"heading","anchor":"%EB%AC%B8%EC%A0%9C%EC%A0%90--%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90","text":"문제점 \/ 고민한 점","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"removeAll() ","type":"text"},{"type":"strong","inlineContent":[{"type":"text","text":"vs"}]},{"text":" 빈 배열 할당하기","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"attribute의 종류를 잘 모르겠네?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Xcode로 커밋을 하는데 한글 자소분리 현상이…","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Numeric과 FloatingPoint 프로토콜이 뭐야?"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Double에는 nan이라는 속성이 있었는데… 뭐였더라…"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"where절을 사용하는 방법과 용도","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"type":"heading","anchor":"%ED%95%B4%EA%B2%B0%EB%B0%A9%EB%B2%95","text":"해결방법","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"removeAll의 코드를 까보니 keepCapacity가 false면 빈배열로 초기화해주고 있었다. 따라서 빈배열을 할당해주는 것과 removeAll은 별반 차이가 없었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"공식문서를 살펴보니 이미 사용해본 속성들이 많았다. 다음에 시간나면 상세히 공부해봐야할 것 같다. 생각보다 꽤나 양이 많았다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Xcode로 커밋하게되면 자소분리 현상 때문에 –amend 키워드로 계속 커밋메세지를 수정해줘야하는 번거로움이 있다. 그래서 SourceTree를 다운받아서 부분 커밋을 할때만 사용하기로 했다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"heading","level":1,"text":"FloatingPoint","anchor":"FloatingPoint"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"부동 소수점 숫자 타입이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"5.5, 100.0 또는 3.14159274와 같은 소수를 나타내는데 사용된다. 표준 라이브러리의 부동 수수점 타입은 사용 가능한 경우 Float, Double, Float80이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"NaN이라는 속성이 있는데, ","type":"text"},{"type":"codeVoice","code":"숫자가 아님"},{"text":"을 반환하는 속성값이다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["let temperatureData = [\"21.5\", \"19.25\", \"27\", \"no data\", \"28.25\", \"no data\", \"23\"]","let tempsCelsius = temperatureData.map { Double($0) ?? .nan }"]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"공식문서 예제에서는 data가 없는 문자열을 에러처리나 옵셔널처리가 아니라 nan으로 표기할 수 있도록 사용하고 있다. 이 예제에서는 nil-coalescing operator (??)를 사용하여 nan을 대체값으로 사용한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"부동 소수점에는 소수점이 있기 때문에 정수형 타입보다 훨씬 더 넓은 범위의 값을 표현할 수 있다. 그리고 Int 타입에 저장할 수 있는 것 보다 훨씬 더 큰 양의 정수나 훨씬 더 작은 음의 정수를 저장할 수도 있다."}]}]}]}]},{"content":[{"level":1,"anchor":"Numeric","text":"Numeric","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Swift에서 계산을 지원하는 정수 또는 부동 소수점이 들어갈 수 있는 프로토콜이다."}]}]},{"content":[{"type":"heading","level":1,"text":"Where 이란?","anchor":"Where-%EC%9D%B4%EB%9E%80"},{"inlineContent":[{"type":"text","text":"특정 패턴과 결합하여 조건을 추가하는 역할을 한다. 조건을 더 추가하고 싶을 때, 특정 타입에 제한을 두고 싶을 때 등등 다양한 용도로 사용된다."}],"type":"paragraph"},{"text":"용도","type":"heading","anchor":"%EC%9A%A9%EB%8F%84","level":2},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"특정 패턴과 결합하여 조건을 추가할 때","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"타입에 대한 제약을 추가할 때"},{"type":"text","text":" "},{"type":"text","text":" "}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"anchor":"%EC%97%98%EB%A6%BC%EC%9D%B4-%EC%95%8C%EB%A0%A4%EC%A4%80-%EB%94%94%EB%B2%84%EA%B9%85-%EA%BF%80%ED%8C%81","text":"엘림이 알려준 디버깅 꿀팁","level":1,"type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"조건문이 짧아도 return은 줄바꿈을 해주는 편이 좋다. 왜냐하면 나중에 디버깅할 때 번거롭기 때문이다. 디버깅을 수월하게 하려면 줄바꿈을 해주는 것이 좋겠다.","type":"text"}]}]}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/array\/3017530-removeall","type":"reference","isActive":true}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/apple\/swift\/blob\/main\/stdlib\/public\/core\/Array.swift","isActive":true,"type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/swift\/floatingpoint"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/TheBasics.html#ID321","isActive":true,"type":"reference"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","isActive":true,"identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/TheBasics.html#ID324"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/swift\/swift_standard_library\/numbers_and_basic_values\/numeric_protocols"}]}]},{"content":[{"inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/swift\/swift_standard_library\/numbers_and_basic_values"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"sections":[],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211109_removeAll,_attribute,_commit,_Numeric,_FloatingPoint,_NaN,_where,_SourceTree","interfaceLanguage":"swift"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211109_removeall,_attribute,_commit,_numeric,_floatingpoint,_nan,_where,_sourcetree"]}],"kind":"article","schemaVersion":{"minor":3,"patch":0,"major":0},"references":{"https://developer.apple.com/documentation/swift/array/3017530-removeall":{"title":"removeAll","titleInlineContent":[{"type":"text","text":"removeAll"}],"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/array\/3017530-removeall","type":"link","url":"https:\/\/developer.apple.com\/documentation\/swift\/array\/3017530-removeall"},"https://github.com/apple/swift/blob/main/stdlib/public/core/Array.swift":{"title":"Array.swift","titleInlineContent":[{"type":"text","text":"Array.swift"}],"identifier":"https:\/\/github.com\/apple\/swift\/blob\/main\/stdlib\/public\/core\/Array.swift","type":"link","url":"https:\/\/github.com\/apple\/swift\/blob\/main\/stdlib\/public\/core\/Array.swift"},"https://developer.apple.com/documentation/swift/floatingpoint":{"title":"FloatingPoint","titleInlineContent":[{"type":"text","text":"FloatingPoint"}],"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/floatingpoint","type":"link","url":"https:\/\/developer.apple.com\/documentation\/swift\/floatingpoint"},"https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html#ID321":{"title":"Floating-Point Numbers","titleInlineContent":[{"type":"text","text":"Floating-Point Numbers"}],"identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/TheBasics.html#ID321","type":"link","url":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/TheBasics.html#ID321"},"https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html#ID324":{"title":"Numeric Type Conversion","titleInlineContent":[{"type":"text","text":"Numeric Type Conversion"}],"identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/TheBasics.html#ID324","type":"link","url":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/TheBasics.html#ID324"},"https://developer.apple.com/documentation/swift/swift_standard_library/numbers_and_basic_values/numeric_protocols":{"title":"Numeric","titleInlineContent":[{"type":"text","text":"Numeric"}],"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/swift_standard_library\/numbers_and_basic_values\/numeric_protocols","type":"link","url":"https:\/\/developer.apple.com\/documentation\/swift\/swift_standard_library\/numbers_and_basic_values\/numeric_protocols"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://developer.apple.com/documentation/swift/swift_standard_library/numbers_and_basic_values":{"title":"Numbers and Basic Values","titleInlineContent":[{"type":"text","text":"Numbers and Basic Values"}],"identifier":"https:\/\/developer.apple.com\/documentation\/swift\/swift_standard_library\/numbers_and_basic_values","type":"link","url":"https:\/\/developer.apple.com\/documentation\/swift\/swift_standard_library\/numbers_and_basic_values"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211111_capture_list,_solid,_protocol,_split,_uml.json b/docs/data/documentation/arinote/211111_capture_list,_solid,_protocol,_split,_uml.json new file mode 100644 index 0000000..2bde8fd --- /dev/null +++ b/docs/data/documentation/arinote/211111_capture_list,_solid,_protocol,_split,_uml.json @@ -0,0 +1 @@ +{"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211111_Capture_List,_SOLID,_protocol,_split,_UML","interfaceLanguage":"swift"},"metadata":{"roleHeading":"Article","title":"211111 Capture List, SOLID, protocol, split, UML","role":"article","modules":[{"name":"Ari's Notes"}]},"sections":[],"schemaVersion":{"patch":0,"major":0,"minor":3},"variants":[{"paths":["\/documentation\/arinote\/211111_capture_list,_solid,_protocol,_split,_uml"],"traits":[{"interfaceLanguage":"swift"}]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","type":"heading","level":1},{"type":"paragraph","inlineContent":[{"type":"text","text":"11월 11일 (목)"}]},{"level":2,"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"캡쳐리스트, 용도가 무엇일까?"}]}]},{"content":[{"inlineContent":[{"text":"계산기 프로젝트 STEP 2의 대한 고민","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"빈 프로토콜의 용도, 프로토콜의 장점","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"SOLID 활동학습","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"text":"고민한 점 \/ 해결 방법","type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2},{"items":[{"content":[{"type":"heading","anchor":"Capture-List","level":1,"text":"Capture List"},{"inlineContent":[{"type":"text","text":"캡쳐리스트는 주변 환경의 범위에서 참조한 변수들을 얼마나 강하게 캡쳐해야하는지를 명시하는 것으로 캡쳐리스트를 사용하여 메모리 누수를 일으키는 강한 참조 순환을 피할 수 있게 된다."},{"text":" ","type":"text"},{"type":"text","text":"캡쳐리스트에 명시한 요소가 참조타입이 아니라면 해당 요소들은 클로저가 생성될 때 초기화 된다."}],"type":"paragraph"},{"anchor":"%EC%9A%A9%EB%8F%84","type":"heading","text":"용도","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"클로저의 강한참조 순환 문제를 해결하기 위해 사용(참조타입)","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"클로저 내부에서 클로저 외부의 값을 참조할 때 참조하는 값이 변경되면 클로저 내부에서도 참조하는 값 또한 바뀌게 되므로 이를 방지하고자 사용(값타입)"}]}]}]},{"anchor":"%EC%BA%A1%EC%B3%90%ED%95%98%EB%8A%94-%EC%8B%9C%EC%A0%90","level":2,"text":"캡쳐하는 시점","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"클로저가 생성되는 시점에 평가된다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"split의 정확한 역할을 유추하지 못했다. 일단 오동나무의 조언에 따라 돌아가게끔 로직을 구성해보았다. 그랬더니 split의 파라미터를 사용하지 않는 로직이 되어버렸다. 리팩토링을 해보려고 했는데, Split의 파라미터를 어떻게 활용해야할지 모르겠다… 리뷰어인 엘림에게 조언을 구해보았으나, 캠퍼들과 함께 고민해보며 해결해나갈 수 밖에 없는 것 같다. 오동나무는 어떤 방향으로 코드를 짜라고 제시해줄수 없다고하고, 야곰은 UML을 이해한 방향으로 짜면 된다고 하니 남은 기한동안 다시한번 고민해보아야 겠다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"빈 프로토콜은 무언가를 묶어줄때…? 협업을 할때 유용한 것 같다."}]},{"level":1,"type":"heading","text":"코다의 설명","anchor":"%EC%BD%94%EB%8B%A4%EC%9D%98-%EC%84%A4%EB%AA%85"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Error 프로토콜을 예시로 하자면, 빈 프로토콜인 Error는 컴파일과 협업을 한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"컴파일러: 니가 어떤일을 할지는 모르겠지만 Error라는 이름을 붙여주면 내가 Error라고 인식해볼게.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"즉 Error는 오직 ","type":"text"},{"inlineContent":[{"type":"text","text":"오류를 표현하기 위한 타입"}],"type":"strong"},{"text":"이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"프로토콜은 규정을 주는 것과 유사하다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"팀원과의 협업 예시"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"A라는 기능과 B라는 기능을 개발중.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"A 기능이 먼저 개발되어야 한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그러나 A의 주요 기능을 프로토콜로 정의해주면 B에서도 A의 기능을 가정하에[?] 병렬적으로 개발을 할 수 있다."}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"계산기 프로젝트로 정리하자면 CalculatorItem 프로토콜을 Double과 Operator가 왜 채택하도록 설정하는지를 고민해보면 이해가 될 것 같다.","type":"text"}],"type":"paragraph"}]},{"content":[{"level":1,"text":"SOLID란?","anchor":"SOLID%EB%9E%80","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"S (Single Responsibility Principle) : 단일 책임 원칙"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"소프트웨어 요소(클래스, 함수 등)는 응집도 있는 하나의 책임을 갖는다. 새 기능을 추가할 때 변경하지말고 새 클래스나 함수를 만들어라","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"O (Open\/Close Principle) : 개방\/폐쇄 원칙"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"소프트웨어 요소는 확장 가능하도록 열려있고 변경에는 닫혀있어야 한다. 새 기능을 추가할 때 변경하지 말고 새 클래스나 함수를 만들어라","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"L (Liscov Substitution Principle) : 리스코브 치환 원칙"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"상속받은 타입은 기본타입으로 대체 가능해야한다. 자식클래스는 부모 클래스 동작을 바꾸지 않는다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"I (Interface Segregation Principle) : 인터페이스 분리 원칙"}]},{"items":[{"content":[{"inlineContent":[{"text":"상위 레벨 모듈은 하위레벨 모듈에 의존하면 안된다. 둘다 추상화 된 인터페이스에 의존해야 한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"D (Dependency Inversion Principle) : 의존성 역전 원칙"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"클라이언트 객체는 사용하지 않는 메소드에 의존하면 안된다.","type":"text"}]}]}],"type":"unorderedList"}]}]},{"level":1,"anchor":"%EC%B6%94%EA%B5%AC%ED%95%B4%EC%95%BC%ED%95%98%EB%8A%94-%EA%B0%80%EC%B9%98","text":"추구해야하는 가치","type":"heading"},{"anchor":"1%EC%88%9C%EC%9C%84-%EA%B0%80%EB%8F%85%EC%84%B1-%EC%BB%A4%EB%AE%A4%EB%8B%88%EC%BC%80%EC%9D%B4%EC%85%98","text":"1순위 가독성, 커뮤니케이션","level":2,"type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"개발자는 코드를 통한 커뮤니케이션"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"읽고 이해할 수 없는 코드가 더욱 가치가 없다"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"heading","level":2,"text":"2순위 단순성","anchor":"2%EC%88%9C%EC%9C%84-%EB%8B%A8%EC%88%9C%EC%84%B1"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"코드는 단순해야 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"커뮤니케이션에 도움이 된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"버그가 생길 틈이 없어진다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"미래의 확장을 위한 복잡한 패턴은 경계의 대상이다","type":"text"}]}]}]},{"anchor":"3%EC%88%9C%EC%9C%84-%EC%9C%A0%EC%97%B0%EC%84%B1","text":"3순위 유연성","level":2,"type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기존의 코드를 수정하는 데에 많은 시간을 소비한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"유연성과 단순성은 trade off","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그리고 “구현 패턴”을 쓴 저자 켄트의 말을 빌리자면 “가치는 원칙보다 높은 수준의 개념이다”, “원칙은 가치를 지키기위해서 존재해야 한다.”","type":"text"}]}]}],"type":"unorderedList"},{"anchor":"%EC%9C%A0%EC%97%B0%EC%84%B1%EC%9D%80-%EA%B7%B8%EB%9E%98%EC%84%9C-%EC%96%B8%EC%A0%9C-%EC%B1%99%EA%B8%B8%EA%B9%8C","text":"유연성은 그래서 언제 챙길까?","type":"heading","level":1},{"inlineContent":[{"type":"text","text":"처음엔 단순하게 > 기획 또는 정책의 변경이 발생 > 이 기획 또는 정책의 변경의 원인은 무엇일까? 그리고 이러한 변경은 계속 발생 가능한 일일까? > 리팩토링을 통해 확장성을 고려해서 변경해보자."}],"type":"paragraph"},{"style":"note","type":"aside","name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"3가지의 가치는 서로 하나의 목표만을 바라보는데 그것은 바로 시간이라고 한다. 프로그래밍 세계에서 비용이란? 즉 비용==시간 이다.","type":"text"}]}]},{"inlineContent":[{"text":"개발에 있어서 가독성, 단순성, 유연성은 모두 개발, 리팩토링하는데 있어 시간을 줄여주기 때문에 매우 중요한 가치다.","type":"text"}],"type":"paragraph"},{"type":"heading","text":"SOLID 원칙을 왜 지켜야 할까?","anchor":"SOLID-%EC%9B%90%EC%B9%99%EC%9D%84-%EC%99%9C-%EC%A7%80%EC%BC%9C%EC%95%BC-%ED%95%A0%EA%B9%8C","level":1},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"높은 코드 품질을 얻기 위해서"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"높은 품질의 코드란 도대체 뭘까? 그리고 높은 품질의 코드는 왜 중요한가?에 대한 고민을 해보자."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"가치는 원칙보다 높은 수준의 개념"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"items":[{"content":[{"type":"heading","anchor":"%EB%B6%B1%EC%9D%B4%EC%9D%98-%EA%B0%95%EC%9D%98%EB%A5%BC-%EB%93%A4%EC%9C%BC%EB%A9%B0-%EC%A4%91%EC%9A%94%ED%95%9C-%EB%B6%80%EB%B6%84-%EB%A9%94%EB%AA%A8","level":1,"text":"붱이의 강의를 들으며 중요한 부분 메모"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"엔지니어는 현재의 상황에서 우선순위를 계산하고 그에 맞는 최선의 선택만을 할 뿐 푀선의 선택을 위해서는 기준을 세워두어야 한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"객체 속성(데이터)을 가져오지 말고 객체가 일하도록 시켜라(연산실행) == 점 두개 쓰지 말라"}],"type":"paragraph"}]}]},{"level":2,"text":"테스트 주도 개발 관련","anchor":"%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%A3%BC%EB%8F%84-%EA%B0%9C%EB%B0%9C-%EA%B4%80%EB%A0%A8","type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"제어흐름이 제대로 되는지에 대한 테스트를 해야한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"제어할 수 없는 부분을 Mock객체를 만들어서 제어할 수 있도록 구현하여 테스트를 한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"영향을 받는 객체들은 Mock을 활용하여 테스트를 한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"병렬적으로 개발을 한다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로토콜 계층이 있으면 가능하다"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"SOLID 원칙으로 코드를 구현한다면 뷰를 나눠서 낄 수 있다. (예시: 콘솔용 뷰, 앱용 뷰)","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"뷰는 언젠간 뚱뚱해질 수 밖에 없다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"성능 측면 보다 코드의 복잡성을 관리하는 것이 어려운 순간이 더 많다."}]}]}]},{"level":2,"type":"heading","anchor":"%EA%B3%B5%EB%B6%80%EB%B2%95","text":"공부법"},{"items":[{"content":[{"inlineContent":[{"text":"최신 트렌드가 중요하다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"중요하다고 하는 것들은 일단 찾아본다. 그걸보고 놓치고 있었다는 사실을 인지한다. 얕게 처음 시작해보고 동시에 코드에서 다뤄보기도 해본다. 시험적인 것을 작게 시도해보고 재미가 없으면 필요한 순간에 다시 돌아온다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"중요해보이는게 많은데 그거를 모든 걸 다해야하는 건 맞지만 뭐부터 할건지 선정하는 것도 중요하다. 그 시기에 해야할 것들은 다른 선배들에게 조언을 구해보는 것도 좋다.","type":"text"}]}]}],"type":"unorderedList"},{"level":2,"type":"heading","anchor":"%ED%85%8C%EC%8A%A4%ED%8A%B8-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%9E%91%EC%84%B1%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%EC%9B%90%EC%B9%99","text":"테스트 가능한 코드를 작성하기 위한 원칙"},{"items":[{"content":[{"inlineContent":[{"text":"반환 값이 있는 것들을 인터페이스로 만드려고 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"반환 값이 없는 것들은 외부 인터페이스로 빼두려고 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"어떻게 하면 테스트 할 수 있을까? 부터 접근하기 시작한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"어떤 메서드가 public하게 있어야 테스트가 가능하겠구나~ 유추해본다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"작은거부터 조립해나가는 경험을 할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Mock의 값이 제대로 전달되었는지도 테스트 해볼 수도 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Mock은 꺼내서 볼 수 있는 존재다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"테스트 거의 대부분 가짜 객체들이 필요하다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"왜 필요하냐면 제어할 수 없는 부분이 있으니 제어할 수 있게 끔 Mock을 만들고 집어넣는다. 그럼 제어할 수 없는 부분을 제어할 수 있게 된다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"-> 약간.. 돼지코 느낌이네…","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"text":"코드를 짤때 우선순위를 정하는 꿀팁","anchor":"%EC%BD%94%EB%93%9C%EB%A5%BC-%EC%A7%A4%EB%95%8C-%EC%9A%B0%EC%84%A0%EC%88%9C%EC%9C%84%EB%A5%BC-%EC%A0%95%ED%95%98%EB%8A%94-%EA%BF%80%ED%8C%81","level":2,"type":"heading"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/ZVT4yFx.png","type":"image"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"여러 군데에서 사용되는 부분."},{"text":" ","type":"text"},{"type":"text","text":"네트워크 같은 부분이 있다면 네트워크를 관리하는 오브젝트, 네트워크를 관리하는 오브젝트는 잘못되면 전반적인 피해를 끼친다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"전반적으로 영향을 끼치는 부분"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"잘 변하지 않는 부분 > 안정적인 테스트를 작성할 수 있는 환경"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터를 가져오는 Model 같은 부분은 잘 안변하고 View 같은 부분은 쉽게 변한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"모델로 보는 부분","type":"codeVoice"},{"text":" 네트워크, 데이터모델, 유저모델, 가위바위보게임 등","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"만약 테스트를 한다면 모델레이어 부터 테스트를 시작한다. 그다음은 컨트롤러, 그다음은 뷰 순서"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"결국 중요한 영역은 잘변하지 않는 영역이 된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"제어가 어려운 영역"},{"text":" UIKit, IBOutlet, IBAction 등","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이런 부분은 보통 과감하게 테스트를 포기한다. 화면 전환 테스트를 예를 들자면 "},{"code":"버튼이 눌렀을 때 전환되야해","type":"codeVoice"},{"text":", ","type":"text"},{"code":"백그라운드 컬러를 설정했을 때 배경색이 잘 변경되야해","type":"codeVoice"},{"text":" 등 이런 부분은 우리가 테스트할 영역이 아니다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"만약에 이런 영역들을 테스트 해야한다면 버튼이 눌렸을 때 이 이벤트를 관리할 수 있는 곳으로 넘겨준다. ","type":"text"},{"type":"codeVoice","code":"버튼이 눌리면 이벤트를 전달해줘!!!"},{"type":"text","text":" 식의 느낌….?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"VC에서 버튼이 눌린다면 View Model에 넘겨준다. 즉 프레젠트를 바로 호출하는게 아니라 데이터를 넘겨준 뒤 호출하는 느낌?"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이런식으로 제어할 수 있는 영역을 늘려간다."}],"type":"paragraph"}]}]},{"level":2,"anchor":"%EB%AC%B4%EC%97%87%EC%9D%84-%EC%9C%84%EC%A3%BC%EB%A1%9C-%EA%B3%B5%EB%B6%80%ED%95%98%EB%A9%B4-%EC%A2%8B%EC%9D%80%EA%B0%80","type":"heading","text":"무엇을 위주로 공부하면 좋은가?"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"신입 입사에 맞는 것을 하면 좋다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"실수를 많이하는 부분을 제일 많이 물어본다. 클래스와 구조체, ARC, 메모리 관리, 쓰레드, GCD, 이벤트가 전달되는 과정 등 이런 부분을 딥하게 알고 있는 것이 좋다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIKit을 예를 들자면 ViewController의 Life Cycle을 디테일하게 알고있는가?"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"예시) ViewWillAppear에서 해서는 안되는 동작들이 ViewDidAppear에서 하는 것들이 있다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"깊은 이해가 중요하고 사고력도 중요하다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"최적화에 관한 질문도 나온다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"실제 사례에서 실험을 해보았는지?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이게 어떻게 되는거지? 라는 정신으로 실험해보는 것도 중요함.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"자료구조 명확히 설명하는 거 중요."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"연습을, 훈련을 극단적으로 해보는 습관을 길러보자."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"SOLID는 다 적용해보는 것 보다 하나씩 적용해보는 걸 해보자.","type":"text"}]}]}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/velog.io\/@kimdo2297\/%ED%81%B4%EB%A1%9C%EC%A0%B8-%EC%BA%A1%EC%B3%90%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-about-closure-capture"}]}]},{"content":[{"inlineContent":[{"text":"붱이의 SOLID 혜자 강의","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://velog.io/@kimdo2297/%ED%81%B4%EB%A1%9C%EC%A0%B8-%EC%BA%A1%EC%B3%90%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-about-closure-capture":{"url":"https:\/\/velog.io\/@kimdo2297\/%ED%81%B4%EB%A1%9C%EC%A0%B8-%EC%BA%A1%EC%B3%90%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-about-closure-capture","type":"link","title":"Capture List","identifier":"https:\/\/velog.io\/@kimdo2297\/%ED%81%B4%EB%A1%9C%EC%A0%B8-%EC%BA%A1%EC%B3%90%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-about-closure-capture","titleInlineContent":[{"type":"text","text":"Capture List"}]},"https://i.imgur.com/ZVT4yFx.png":{"variants":[{"url":"https:\/\/i.imgur.com\/ZVT4yFx.png","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/i.imgur.com\/ZVT4yFx.png","alt":null},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211112_delegation,_numberformatter,_finding_and_refactoring_code,_snapkit.json b/docs/data/documentation/arinote/211112_delegation,_numberformatter,_finding_and_refactoring_code,_snapkit.json new file mode 100644 index 0000000..783d9fe --- /dev/null +++ b/docs/data/documentation/arinote/211112_delegation,_numberformatter,_finding_and_refactoring_code,_snapkit.json @@ -0,0 +1 @@ +{"sections":[],"kind":"article","metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"title":"211112 Delegation, NumberFormatter, Finding and Refactoring Code, SnapKit","roleHeading":"Article"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"primaryContentSections":[{"content":[{"type":"heading","text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned","level":1},{"inlineContent":[{"type":"text","text":"11월 12일 (금)"}],"type":"paragraph"},{"type":"heading","text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Delegation Patten의 복습과 이해"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"NumberFormatter 학습","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","type":"heading"},{"type":"unorderedList","items":[{"content":[{"anchor":"ViewController%EC%97%90%EC%84%9C%EC%9D%98-%EA%B0%84%EB%8B%A8%ED%95%9C-%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95-%EC%A0%95%EB%A6%AC","level":3,"type":"heading","text":"ViewController에서의 간단한 사용방법 정리"},{"items":[{"content":[{"inlineContent":[{"text":"Main에서 신호를 받아서 처리해야할 일을 protocol에 메서드를 정의해주고, 채택하여 준수한다. (메서드 구현)","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Main에서 SubView로 넘어가기전에 자신(Main)을 "},{"code":"delegate = self","type":"codeVoice"},{"type":"text","text":" 라고 알려준 후 다음 화면으로 넘어가게 된다면 다음 화면에서 protocol로 정의해준 메소드를 사용할 수 있게 된다. 이때 delegate는 "},{"code":"protocol 타입","type":"codeVoice"},{"text":"이다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"text":"코드로 이해해보기","anchor":"%EC%BD%94%EB%93%9C%EB%A1%9C-%EC%9D%B4%ED%95%B4%ED%95%B4%EB%B3%B4%EA%B8%B0","level":3,"type":"heading"},{"type":"codeListing","code":["protocol BossDelegate {"," func wakeUp() \/\/ 일어나는 일을 위임"," \/\/ 비서를 시켜서 자신을 깨우게 할 것임.","}","","protocol SecretaryDelegate {"," var boss: BossDelegate? { get set } \/\/ 보스를 위임"," \/\/ 왜냐하면 보스는 바뀔 수도 있다는 전제하에 확장성을 열어둔 것","}","","class Boss: BossDelegate {"," var secretary: SecretaryDelegate"," var isSleeping: Bool = true",""," init(secretary: SecretaryDelegate) {"," \/\/ 보스를 초기화 시 비서의 보스는 바로 나 자신이라고 알려주기"," self.secretary = secretary"," self.secretary.boss = self"," }",""," func wakeUp() { \/\/ 비서가 해줄 일"," isSleeping = false"," print(\"보스: 아...꿀잠잤다~~~~\")"," }","}","","class Secretary: SecretaryDelegate {"," var boss: BossDelegate? \/\/ 보스를 위임받음",""," func wakeUpBoss() { \/\/ 비서가 하는 일"," print(\"비서: 사장님... 일어나셔야죠...!!!\")"," boss?.wakeUp() \/\/ 보스의 일(wakeUp)을 대신해줌"," }","}","let secretary = Secretary() \/\/ 보스가 없는 상태인데","let ari = Boss(secretary: secretary) \/\/ 여기서 보스가 생김, 동시에 보스는 비서가 생겼음","","secretary.wakeUpBoss()","\/*"," 비서: 사장님... 일어나셔야죠...!!!"," 보스: 아...꿀잠잤다~~~~"," *\/"],"syntax":"swift"},{"type":"paragraph","inlineContent":[{"text":"위 예제에서도 확인할 수 있듯이 서로를 위임하고 있는 형태이다. 보통은 단방향으로 위임하여도 의존성이 해결된다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":"(예: 사장은 비서(secretary)를 알필요가 없다.)"}]}]},{"content":[{"type":"heading","anchor":"NumberFormatter","level":3,"text":"NumberFormatter"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"최대 자릿수 설정하는 방법","type":"text"}]}]}]},{"syntax":"swift","code":["let numberFormatter = NumberFormatter()","numberFormatter.numberStyle = .decimal","numberFormatter.maximumSignificantDigits = 10","let price = 1234567.123456789123422222222","let result = numberFormatter.string(for: price)!","print(result) \/\/ 1,234,567.123"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"잘리는 숫자를 버릴지 반올림할지 설정하는 방법"}]}]}]},{"syntax":"swift","type":"codeListing","code":["let numberFormatter = NumberFormatter()","numberFormatter.numberStyle = .decimal","numberFormatter.maximumSignificantDigits = 10","numberFormatter.roundingMode = .up \/\/ 반올림 모드","let price = 1234567.123546789123422222222","let result = numberFormatter.string(for: price)!","print(result) \/\/ 1,234,567.124"]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"몇자리마다 쉼표를 표기해주는 방법"}],"type":"paragraph"}]}]},{"type":"codeListing","syntax":"swift","code":["let numberFormatter = NumberFormatter()","numberFormatter.numberStyle = .decimal","let price = 1234567898123456789","let result = numberFormatter.string(for: price)!","print(result) \/\/ 1,234,567,898,123,456,789"]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"계산기 프로젝트를 위한 실험","type":"text"}]}]}]},{"syntax":"swift","code":["let numberFormatter = NumberFormatter()","numberFormatter.numberStyle = .decimal","numberFormatter.maximumSignificantDigits = 20","numberFormatter.roundingMode = .up \/\/ 반올림","let price = 123.123546789123422222222 \/\/ 정수와 소수점 합쳐서 20개로 잘리는지 #1","let price2 = 1234567.123546789123422222222 \/\/ 정수와 소수점 합쳐서 20개로 잘리는지 #2","let price3 = 1234567.12340000 \/\/ 소수점이 잘리는지","let result = numberFormatter.string(for: price)!","let result2 = numberFormatter.string(for: price2)!","let result3 = numberFormatter.string(for: price3)!","print(result) \/\/ 123.12354678912342","print(result2) \/\/ 1,234,567.1235467892","print(result3) \/\/ 1,234,567.1234"],"type":"codeListing"}]}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"level":2,"text":"토요스터디 꿀팁","type":"heading","anchor":"%ED%86%A0%EC%9A%94%EC%8A%A4%ED%84%B0%EB%94%94-%EA%BF%80%ED%8C%81"},{"inlineContent":[{"text":"Wody의 라이브코딩을 보며 얻은 꿀팁이다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"메서드 내부에 일부분을 바로 빼서 다른 메소드로 만드는 방법"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"분리하고 싶은 코드를 드래그 후 우클릭하여 Refactor > Extract to Method를 클릭하면 메서드로 추출할 수 있다.","type":"text"}]}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/CwPLuyA.png"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"오토레이아웃을 전부 익혀두면 편리한 프레임워크"}],"type":"paragraph"},{"items":[{"content":[{"level":3,"anchor":"SnapKit","type":"heading","text":"SnapKit"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"짧은 코드로 오토레이아웃을 표현할 수 있도록 도와주는 프레임워크다. 아직 지금은 오토레이아웃을 정복하지 못했기 때문에 사용하긴 이른거 같고, 나중에 익숙해졌을 때 필요하게 될 것 같다."}]}]}]}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Wody의 프로토콜 강의😎"}]}]},{"content":[{"inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/numberformatter"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/numberformatter\/roundingmode","type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/xcode\/finding-and-refactoring-code","isActive":true}]}]},{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/github.com\/SnapKit\/SnapKit","isActive":true}],"type":"paragraph"}]}]}]}]}],"kind":"content"}],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211112_delegation,_numberformatter,_finding_and_refactoring_code,_snapkit"]}],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211112_Delegation,_NumberFormatter,_Finding_and_Refactoring_Code,_SnapKit"},"schemaVersion":{"major":0,"minor":3,"patch":0},"references":{"https://i.imgur.com/CwPLuyA.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/CwPLuyA.png"}],"type":"image","identifier":"https:\/\/i.imgur.com\/CwPLuyA.png","alt":null},"https://developer.apple.com/documentation/foundation/numberformatter/roundingmode":{"titleInlineContent":[{"type":"text","text":"NumberFormatter-roundingmode"}],"url":"https:\/\/developer.apple.com\/documentation\/foundation\/numberformatter\/roundingmode","type":"link","title":"NumberFormatter-roundingmode","identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/numberformatter\/roundingmode"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://developer.apple.com/documentation/xcode/finding-and-refactoring-code":{"titleInlineContent":[{"type":"text","text":"Finding and Refactoring Code"}],"url":"https:\/\/developer.apple.com\/documentation\/xcode\/finding-and-refactoring-code","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/xcode\/finding-and-refactoring-code","title":"Finding and Refactoring Code"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://developer.apple.com/documentation/foundation/numberformatter":{"titleInlineContent":[{"type":"text","text":"NumberFormatter"}],"url":"https:\/\/developer.apple.com\/documentation\/foundation\/numberformatter","type":"link","title":"NumberFormatter","identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/numberformatter"},"https://github.com/SnapKit/SnapKit":{"titleInlineContent":[{"type":"text","text":"SnapKit"}],"url":"https:\/\/github.com\/SnapKit\/SnapKit","type":"link","identifier":"https:\/\/github.com\/SnapKit\/SnapKit","title":"SnapKit"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211115_lldb,_complie,_build,_link,_split,_components,_import,_get.json b/docs/data/documentation/arinote/211115_lldb,_complie,_build,_link,_split,_components,_import,_get.json new file mode 100644 index 0000000..2b4dba1 --- /dev/null +++ b/docs/data/documentation/arinote/211115_lldb,_complie,_build,_link,_split,_components,_import,_get.json @@ -0,0 +1 @@ +{"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"211115 LLDB, Complie, Build, Link, split, components, import, get"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Xcode"]]},"schemaVersion":{"patch":0,"major":0,"minor":3},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211115_LLDB,_Complie,_Build,_Link,_split,_components,_import,_get"},"kind":"article","sections":[],"primaryContentSections":[{"content":[{"type":"heading","level":1,"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"type":"text","text":"11월 15일 (월)"}]},{"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"LLDB 활동학습","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"디버깅 직접 해보기"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"STEP 2 피드백 확인하며 고민해보기","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"text":"고민한 점 \/ 해결 방법","type":"heading","level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"heading","level":3,"text":"컴파일(Complie)이란?","anchor":"%EC%BB%B4%ED%8C%8C%EC%9D%BCComplie%EC%9D%B4%EB%9E%80"},{"inlineContent":[{"type":"text","text":"개발자가 작성한 소스코드를 바이너리 코드로 변환하는 과정"},{"text":" ","type":"text"},{"type":"text","text":"즉, 컴퓨터가 이해할 수 있는 기계어로 변환하는 작업이다."}],"type":"paragraph"},{"level":3,"type":"heading","text":"빌드(Build)란?","anchor":"%EB%B9%8C%EB%93%9CBuild%EB%9E%80"},{"type":"paragraph","inlineContent":[{"text":"소스코드 파일을 실행가능한 소프트웨어 산출물로 만드는 일련의 과정을 말한다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"빌드의 단계 중 컴파일이 포함되어 있는데 컴파일은 빌드의 부분집합이라 할 수 있다. 빌드과정을 도와주는 도구는 빌드 툴이라 한다."}]},{"type":"heading","level":3,"text":"링크(Link)란?","anchor":"%EB%A7%81%ED%81%ACLink%EB%9E%80"},{"inlineContent":[{"text":"프로젝트를 진행하다보면 소스파일이 여러개가 생성이 되고 A라는 소스파일에서 B라는 소스파일에 존재하는 함수를 호출하는 경우가 있다. 이 때 A와 B 소스파일 각각 컴파일만 하면 A가 B에 존재하는 함수를 찾질 못하기 때문에 호출할 수가 없다.","type":"text"},{"type":"text","text":" "},{"text":"따라서 A와 B를 연결해주는 작업이 필요한데 이 작업을 링크라고 한다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":"여러개로 분리된 소스파일들을 컴파일한 결과물들에서 최종 실행가능한 파일을 만들기 위해 필요한 부분을 찾아서 연결해주는 작업이다."}],"type":"paragraph"},{"type":"heading","level":3,"text":"빌드 툴(Build tool)","anchor":"%EB%B9%8C%EB%93%9C-%ED%88%B4Build-tool"},{"type":"paragraph","inlineContent":[{"type":"text","text":"일반적으로 빌드 툴이 제공해주는 기능으로는 다음과 같은 기능들이 있다."},{"text":" ","type":"text"},{"type":"text","text":"전처리(Preprocess) > 컴파일(Complie) > 링크(Link)"},{"text":" ","type":"text"},{"text":"패키징(packaging), 테스팅(testing), 배포(distribution) 등..","type":"text"},{"text":" ","type":"text"},{"text":"빌드 툴로는 Ant, Maven, Gradle 등이 있다.","type":"text"}]},{"text":"기계어 번역 방식에 따른 프로그래밍 언어의 분류","anchor":"%EA%B8%B0%EA%B3%84%EC%96%B4-%EB%B2%88%EC%97%AD-%EB%B0%A9%EC%8B%9D%EC%97%90-%EB%94%B0%EB%A5%B8-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%96%B8%EC%96%B4%EC%9D%98-%EB%B6%84%EB%A5%98","level":2,"type":"heading"},{"anchor":"%EC%BB%B4%ED%8C%8C%EC%9D%BC-%EC%96%B8%EC%96%B4","text":"컴파일 언어","level":3,"type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"구현체들이 일반적으로 컴파일러(소스코드로부터 기계어를 생성해내는 변환기)이면서 인터프리터(런타임 전 변환 과정을 거치지 않는 소스 코드의 단계별 실행기)가 아닌 프로그래밍 언어."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"C, C++, Go …","type":"text"}]}]}]},{"level":3,"anchor":"%EB%B0%94%EC%9D%B4%ED%8A%B8%EC%BD%94%EB%93%9C-%EC%96%B8%EC%96%B4","text":"바이트코드 언어","type":"heading"},{"type":"paragraph","inlineContent":[{"text":"특정 하드웨어가 아닌 가상 컴퓨터에서 돌아가는 실행 프로그램을 위한 이진 표현법","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Java, C# …","type":"text"}]}]}]},{"type":"heading","level":3,"text":"인터프리터 언어","anchor":"%EC%9D%B8%ED%84%B0%ED%94%84%EB%A6%AC%ED%84%B0-%EC%96%B8%EC%96%B4"},{"type":"paragraph","inlineContent":[{"text":"프로그램 언어의 소스코드를 바로 실행하는 컴퓨터 프로그램 또는 환경을 말한다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"BASIC, JavaScript, Python, Ruby …","type":"text"}]}]}]},{"type":"thematicBreak"},{"text":"ViewController.swift 파일의 23번째 줄에 브레이크 포인트를 설정하려면 입력해야 하는 LLDB 명령어는?","anchor":"ViewControllerswift-%ED%8C%8C%EC%9D%BC%EC%9D%98-23%EB%B2%88%EC%A7%B8-%EC%A4%84%EC%97%90-%EB%B8%8C%EB%A0%88%EC%9D%B4%ED%81%AC-%ED%8F%AC%EC%9D%B8%ED%8A%B8%EB%A5%BC-%EC%84%A4%EC%A0%95%ED%95%98%EB%A0%A4%EB%A9%B4-%EC%9E%85%EB%A0%A5%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-LLDB-%EB%AA%85%EB%A0%B9%EC%96%B4%EB%8A%94","level":3,"type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"(lldb) breakpoint set -file viewController.swift –line 23"}]}]},{"content":[{"inlineContent":[{"text":"(lldb) br s -file viewController.swift –line 23","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"(lldb) b ViewController.swift:23"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"(lldb) b 23"}]}]}],"type":"unorderedList"},{"text":"Breakpoint Navigator를 통해 titleLabel의 text가 “두 번째 뷰 컨트롤러!“인 경우에만 작동을 일시정지하고 titleLabel의 text를 출력하는 액션을 실행하도록 설정해보세요","level":3,"anchor":"Breakpoint-Navigator%EB%A5%BC-%ED%86%B5%ED%95%B4-titleLabel%EC%9D%98-text%EA%B0%80-%EB%91%90-%EB%B2%88%EC%A7%B8-%EB%B7%B0-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC%EC%9D%B8-%EA%B2%BD%EC%9A%B0%EC%97%90%EB%A7%8C-%EC%9E%91%EB%8F%99%EC%9D%84-%EC%9D%BC%EC%8B%9C%EC%A0%95%EC%A7%80%ED%95%98%EA%B3%A0-titleLabel%EC%9D%98-text%EB%A5%BC-%EC%B6%9C%EB%A0%A5%ED%95%98%EB%8A%94-%EC%95%A1%EC%85%98%EC%9D%84-%EC%8B%A4%ED%96%89%ED%95%98%EB%8F%84%EB%A1%9D-%EC%84%A4%EC%A0%95%ED%95%B4%EB%B3%B4%EC%84%B8%EC%9A%94","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"breakpoint를 잡고나서 우클릭을 한 후 "},{"code":"Edit Breakpoint...","type":"codeVoice"},{"text":"를 클릭","type":"text"}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/xn3B9R1.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이후 Condition을 기재해주고 엔터를 한다."}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/bPLHhvx.png"}]}]},{"content":[{"inlineContent":[{"text":"Add Action을 눌러서 출력하는 액션을 만드는 것 같다[?]","type":"text"}],"type":"paragraph"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/VEqeNS5.png"}],"type":"paragraph"}]}]},{"text":"View Controller의 사용자 눈에 보이지 않는 뷰의 오토레이아웃 제약을 확인하기","level":3,"type":"heading","anchor":"View-Controller%EC%9D%98-%EC%82%AC%EC%9A%A9%EC%9E%90-%EB%88%88%EC%97%90-%EB%B3%B4%EC%9D%B4%EC%A7%80-%EC%95%8A%EB%8A%94-%EB%B7%B0%EC%9D%98-%EC%98%A4%ED%86%A0%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%EC%A0%9C%EC%95%BD%EC%9D%84-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/8USVLJf.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/DAtArE5.png"}]}]}],"type":"unorderedList"},{"type":"heading","text":"디버그 모드로 실행중인 상태에서 사용자 눈에 보이지 않는 뷰의 색상을 분홍색으로 변겅해보세요","anchor":"%EB%94%94%EB%B2%84%EA%B7%B8-%EB%AA%A8%EB%93%9C%EB%A1%9C-%EC%8B%A4%ED%96%89%EC%A4%91%EC%9D%B8-%EC%83%81%ED%83%9C%EC%97%90%EC%84%9C-%EC%82%AC%EC%9A%A9%EC%9E%90-%EB%88%88%EC%97%90-%EB%B3%B4%EC%9D%B4%EC%A7%80-%EC%95%8A%EB%8A%94-%EB%B7%B0%EC%9D%98-%EC%83%89%EC%83%81%EC%9D%84-%EB%B6%84%ED%99%8D%EC%83%89%EC%9C%BC%EB%A1%9C-%EB%B3%80%EA%B2%85%ED%95%B4%EB%B3%B4%EC%84%B8%EC%9A%94","level":3},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"inlineContent":[{"type":"text","text":"expression"}],"type":"strong"},{"text":" ","type":"text"},{"text":"Command는 Runtime에 여러 정보를 출력할 수 있을 뿐 아니라 값을 변경해 줄수도 있다. LLDB는 내부적으로 값이 출력될 때마다 local variable을 $R-의 형태로 만들어 저장한다. 이 값들은 해당 break context가 벗어나도 사용 가능한 값들이고 심지어 수정해서 사용할 수도 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"(lldb) e self.view.subviews"},{"type":"text","text":" "},{"type":"text","text":"([UIView]) $R1 = 3 values {"},{"type":"text","text":" "},{"type":"text","text":"[0] = 0x0000000153609ec0"},{"type":"text","text":" "},{"type":"text","text":"[1] = 0x0000000153617fd0"},{"text":" ","type":"text"},{"type":"text","text":"[2] = 0x00000001536182b0"},{"text":" ","type":"text"},{"text":"}","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"(lldb) e $R1[0].backgroundColor = UIColor.systemPink"},{"type":"text","text":" "},{"type":"text","text":"() $R2 = {}"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"(lldb) continue"}]}]}]},{"type":"heading","anchor":"%EB%91%90-%EB%B2%88%EC%A7%B8-%EB%B7%B0-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC%EC%9D%98-%EB%B7%B0%EA%B0%80-%ED%99%94%EB%A9%B4%EC%97%90-%ED%91%9C%EC%8B%9C%EB%90%9C-%EC%83%81%ED%83%9C%EC%97%90%EC%84%9C-%EB%91%90-%EB%B2%88%EC%A7%B8-%EB%B7%B0-%EC%BB%A8%ED%8A%B8%EB%A1%A4%EB%9F%AC-%EA%B9%8C%EC%A7%80%EC%9D%98-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B7%B8%EB%9E%98%ED%94%84%EB%A5%BC-%EC%BA%A1%EC%B3%90%ED%95%B4%EB%B3%B4%EA%B8%B0","level":3,"text":"두 번째 뷰 컨트롤러의 뷰가 화면에 표시된 상태에서, 두 번째 뷰 컨트롤러 까지의 메모리 그래프를 캡쳐해보기"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/SfMvKXO.png"}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Ti8Kutb.png"}],"type":"paragraph"}]}]},{"anchor":"LLDB%EC%9D%98-%ED%8A%B9%EC%A0%95-%EB%AA%85%EB%A0%B9%EC%96%B4%EC%9D%98-%EB%B3%84%EC%B9%AD%EC%9D%84-%EC%84%A4%EC%A0%95%ED%95%B4%EC%A4%84-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%AA%85%EB%A0%B9%EC%96%B4%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C","text":"LLDB의 특정 명령어의 별칭을 설정해줄 수 있는 명령어는 무엇일까?","level":3,"type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"alias"}]}]},{"content":[{"inlineContent":[{"text":"(lldb) command alias 별명 “줄이고 싶은 Command”","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"(lldb) command alias pojc expression -l objc -O –","type":"text"}]}]}],"type":"unorderedList"},{"text":"LLDB의 v, po, p 명령어의 차이에 대해 알아봅시다","level":3,"type":"heading","anchor":"LLDB%EC%9D%98-v-po-p-%EB%AA%85%EB%A0%B9%EC%96%B4%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B4%85%EC%8B%9C%EB%8B%A4"},{"code":[" v -- Show variables for the current stack frame. Defaults to all"," po -- Evaluate an expression on the current thread. Displays any"," p -- Evaluate an expression on the current thread. ​Displays any","","'po' is an abbreviation for 'expression -O --'","'p' is an abbreviation for 'expression --'","'v' is an abbreviation for 'frame variable'"],"syntax":null,"type":"codeListing"},{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"실제로 차이점 테스트 해보기"}]},{"type":"codeListing","code":["(lldb) po titleLabel.text","▿ Optional"," - some : \"두 번째 뷰 컨트롤러!\"","","(lldb) p titleLabel.text","(String?) $R1 = some {"," _guts = {"," _object = (_countAndFlagsBits = 12, _object = 0x50006000036c88a0)"," }","}","(lldb) "],"syntax":null},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/lLtyk8v.png"}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"text":"split vs components","type":"heading","level":2,"anchor":"split-vs-components"},{"text":"import 여부","anchor":"import-%EC%97%AC%EB%B6%80","type":"heading","level":3},{"items":[{"content":[{"inlineContent":[{"text":"split은 swift 표준 라이브러리에 속해있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"components는 Foundation 프레임워크에 속해있어 import하여 사용할 수 있다."}]}]}],"type":"unorderedList"},{"type":"heading","text":"파라미터","level":3,"anchor":"%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"split(separator: Character, maxSplits: Int = Int.max, omittingEmptySubsequences: Bool = true)"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"components(separatedBy separator: String)","type":"text"}]}]}]},{"text":"공백으로 처리할 때의 다른 결과","anchor":"%EA%B3%B5%EB%B0%B1%EC%9C%BC%EB%A1%9C-%EC%B2%98%EB%A6%AC%ED%95%A0-%EB%95%8C%EC%9D%98-%EB%8B%A4%EB%A5%B8-%EA%B2%B0%EA%B3%BC","type":"heading","level":3},{"type":"codeListing","code":[" let str = \"My name is Sudhir \" \/\/ trailing space",""," str.split(separator: \" \")"," \/\/ [\"My\", \"name\", \"is\", \"Sudhir\"]",""," str.components(separatedBy: \" \")"," \/\/ [\"My\", \"name\", \"is\", \"Sudhir\", \"\"] ← Additional empty string"],"syntax":"swift"},{"type":"paragraph","inlineContent":[{"text":"둘다 동일한 결과가 나오게 하려면 split의 파라미터 ","type":"text"},{"type":"codeVoice","code":"omittingEmptySubsequences"},{"type":"text","text":"를 false로 옵션을 따로 줘야 가능하다."}]},{"code":["str.split(separator: \" \", omittingEmptySubsequences: false)"," \/\/ [\"My\", \"name\", \"is\", \"Sudhir\", \"\"]"],"type":"codeListing","syntax":"swift"},{"type":"heading","text":"반환타입의 차이","anchor":"%EB%B0%98%ED%99%98%ED%83%80%EC%9E%85%EC%9D%98-%EC%B0%A8%EC%9D%B4","level":3},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"split -> [Substring]"}]}]},{"content":[{"inlineContent":[{"text":"components -> [String]","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"heading","anchor":"%EC%84%B1%EB%8A%A5-%EC%B0%A8%EC%9D%B4","level":3,"text":"성능 차이"},{"inlineContent":[{"type":"text","text":"반환타입에서 볼 수 있듯 "},{"type":"codeVoice","code":"split"},{"type":"text","text":"은 원본 문자열을 참조(SubString)하고 있기 때문에, 새 문자열을 할당하지 않는다."},{"type":"text","text":" "},{"type":"text","text":"따라서 split이 components보다 성능측면에서 빠르다고 볼 수 있다."}],"type":"paragraph"},{"name":"Note","content":[{"inlineContent":[{"text":"Substring이란?","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"원본 문자열의 메모리를 공유한다."},{"text":" ","type":"text"},{"text":"값을 읽기만 할 때는 원본메모리를 공유하고, 값을 변경하는 시점에만 새로운 메모리가 생성된다.","type":"text"}],"type":"paragraph"}],"type":"aside","style":"note"},{"type":"paragraph","inlineContent":[{"text":"Substring은 주로 문자열을 처리할 때 메모리를 절약하기 위해서 쓰이는 타입으로 알고있다!","type":"text"}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"type":"thematicBreak"},{"level":2,"type":"heading","anchor":"%EB%A9%94%EC%86%8C%EB%93%9C%EB%AA%85%EC%97%90-get%EC%9D%84-%EC%93%B0%EB%8A%94-%EA%B2%83%EC%9D%84-%EC%99%9C-%EC%A7%80%EC%96%91%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C","text":"메소드명에 get을 쓰는 것을 왜 지양해야할까?"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"isActive":true,"identifier":"https:\/\/github.com\/yagom-academy\/ios-calculator-app\/pull\/87#discussion_r749280823","type":"reference"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"heading","anchor":"%ED%95%B4%EA%B2%B0%ED%95%98%EC%A7%80-%EB%AA%BB%ED%95%9C-%EC%A0%90","text":"해결하지 못한 점","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"디버깅 시 오류(Error) 혹은 익셉션(Exception)이 발생한 경우 프로세스의 동작을 멈추도록 하는 방법","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/freezboi.tistory.com\/39"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/ko.wikipedia.org\/wiki\/%EC%BB%B4%ED%8C%8C%EC%9D%BC_%EC%96%B8%EC%96%B4"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/ko.wikipedia.org\/wiki\/%EB%B0%94%EC%9D%B4%ED%8A%B8%EC%BD%94%EB%93%9C","isActive":true}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/ko.wikipedia.org\/wiki\/%EC%9D%B8%ED%84%B0%ED%94%84%EB%A6%AC%ED%84%B0","isActive":true,"type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/yagom.net\/courses\/start-lldb\/"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/stackoverflow.com\/questions\/46344649\/componentseparatedby-versus-splitseparator","type":"reference","isActive":true}]}]}]}]}]}],"kind":"content"}],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211115_lldb,_complie,_build,_link,_split,_components,_import,_get"]}],"references":{"https://ko.wikipedia.org/wiki/%EC%9D%B8%ED%84%B0%ED%94%84%EB%A6%AC%ED%84%B0":{"url":"https:\/\/ko.wikipedia.org\/wiki\/%EC%9D%B8%ED%84%B0%ED%94%84%EB%A6%AC%ED%84%B0","title":"인터프리터 언어","type":"link","titleInlineContent":[{"text":"인터프리터 언어","type":"text"}],"identifier":"https:\/\/ko.wikipedia.org\/wiki\/%EC%9D%B8%ED%84%B0%ED%94%84%EB%A6%AC%ED%84%B0"},"https://github.com/yagom-academy/ios-calculator-app/pull/87#discussion_r749280823":{"url":"https:\/\/github.com\/yagom-academy\/ios-calculator-app\/pull\/87#discussion_r749280823","title":"링크","type":"link","titleInlineContent":[{"type":"text","text":"링크"}],"identifier":"https:\/\/github.com\/yagom-academy\/ios-calculator-app\/pull\/87#discussion_r749280823"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/lLtyk8v.png":{"variants":[{"url":"https:\/\/i.imgur.com\/lLtyk8v.png","traits":["1x","light"]}],"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/lLtyk8v.png"},"https://i.imgur.com/Ti8Kutb.png":{"variants":[{"url":"https:\/\/i.imgur.com\/Ti8Kutb.png","traits":["1x","light"]}],"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/Ti8Kutb.png"},"https://i.imgur.com/xn3B9R1.png":{"variants":[{"url":"https:\/\/i.imgur.com\/xn3B9R1.png","traits":["1x","light"]}],"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/xn3B9R1.png"},"https://i.imgur.com/VEqeNS5.png":{"variants":[{"url":"https:\/\/i.imgur.com\/VEqeNS5.png","traits":["1x","light"]}],"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/VEqeNS5.png"},"https://freezboi.tistory.com/39":{"url":"https:\/\/freezboi.tistory.com\/39","title":"컴파일과 빌드 차이점","type":"link","titleInlineContent":[{"text":"컴파일과 빌드 차이점","type":"text"}],"identifier":"https:\/\/freezboi.tistory.com\/39"},"https://i.imgur.com/bPLHhvx.png":{"variants":[{"url":"https:\/\/i.imgur.com\/bPLHhvx.png","traits":["1x","light"]}],"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/bPLHhvx.png"},"https://i.imgur.com/SfMvKXO.png":{"variants":[{"url":"https:\/\/i.imgur.com\/SfMvKXO.png","traits":["1x","light"]}],"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/SfMvKXO.png"},"https://ko.wikipedia.org/wiki/%EC%BB%B4%ED%8C%8C%EC%9D%BC_%EC%96%B8%EC%96%B4":{"url":"https:\/\/ko.wikipedia.org\/wiki\/%EC%BB%B4%ED%8C%8C%EC%9D%BC_%EC%96%B8%EC%96%B4","title":"컴파일 언어","type":"link","titleInlineContent":[{"type":"text","text":"컴파일 언어"}],"identifier":"https:\/\/ko.wikipedia.org\/wiki\/%EC%BB%B4%ED%8C%8C%EC%9D%BC_%EC%96%B8%EC%96%B4"},"https://yagom.net/courses/start-lldb/":{"url":"https:\/\/yagom.net\/courses\/start-lldb\/","title":"LLDB 정복","type":"link","titleInlineContent":[{"text":"LLDB 정복","type":"text"}],"identifier":"https:\/\/yagom.net\/courses\/start-lldb\/"},"https://i.imgur.com/8USVLJf.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/8USVLJf.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/8USVLJf.png","type":"image"},"https://stackoverflow.com/questions/46344649/componentseparatedby-versus-splitseparator":{"title":"split vs components","titleInlineContent":[{"type":"text","text":"split vs components"}],"url":"https:\/\/stackoverflow.com\/questions\/46344649\/componentseparatedby-versus-splitseparator","identifier":"https:\/\/stackoverflow.com\/questions\/46344649\/componentseparatedby-versus-splitseparator","type":"link"},"https://i.imgur.com/DAtArE5.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/DAtArE5.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/DAtArE5.png","type":"image"},"doc://AriNote/documentation/AriNote/Xcode":{"role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Xcode","url":"\/documentation\/arinote\/xcode","title":"Xcode","kind":"article","abstract":[{"text":"언젠가…. 좋아질거지 Xcode…?","type":"text"}],"type":"topic"},"https://ko.wikipedia.org/wiki/%EB%B0%94%EC%9D%B4%ED%8A%B8%EC%BD%94%EB%93%9C":{"title":"바이트코드 언어","titleInlineContent":[{"text":"바이트코드 언어","type":"text"}],"url":"https:\/\/ko.wikipedia.org\/wiki\/%EB%B0%94%EC%9D%B4%ED%8A%B8%EC%BD%94%EB%93%9C","identifier":"https:\/\/ko.wikipedia.org\/wiki\/%EB%B0%94%EC%9D%B4%ED%8A%B8%EC%BD%94%EB%93%9C","type":"link"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211116_scroll_view,_stack_view,_setcontentoffset,_layoutifneeded,_setneedslayout.json b/docs/data/documentation/arinote/211116_scroll_view,_stack_view,_setcontentoffset,_layoutifneeded,_setneedslayout.json new file mode 100644 index 0000000..4039179 --- /dev/null +++ b/docs/data/documentation/arinote/211116_scroll_view,_stack_view,_setcontentoffset,_layoutifneeded,_setneedslayout.json @@ -0,0 +1 @@ +{"schemaVersion":{"minor":3,"patch":0,"major":0},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211116_Scroll_View,_Stack_View,_setContentOffset,_layoutIfNeeded,_setNeedsLayout","interfaceLanguage":"swift"},"metadata":{"title":"211116 Scroll View, Stack View, setContentOffset, layoutIfNeeded, setNeedsLayout","role":"article","roleHeading":"Article","modules":[{"name":"Ari's Notes"}]},"primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","type":"heading","level":1},{"type":"paragraph","inlineContent":[{"type":"text","text":"11월 16일 (화)"}]},{"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"계산기 프로젝트 STEP 3 구현해보기","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Scroll View, Stack View","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"계산내역이 상단 공간을 넘어 이어지는 경우 자동 스크롤 하는 방법"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"STEP 3에 대한 스포일러가 있으니 주의하세요.","type":"text"},{"type":"text","text":" "},{"text":" ","type":"text"}]}]}],"type":"unorderedList"}]}]},{"type":"heading","level":2,"text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"items":[{"content":[{"level":3,"anchor":"Stack-View%EB%9E%80","text":"Stack View란?","type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/UztL3Aj.png","type":"image"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Auto Layout을 이용해 열 또는 행에 View들의 묶음을 배치할 수 있는 간소화 된 인터페이스"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Stack View는 Arranged Subviews 프로퍼티에 들어있는 모든 뷰의 레이아웃을 관리한다. 이 뷰들은 StackView의 arrangedSubviews 배열의 순서에 따라 StackView의 축(axis)을 따라 배치된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"레이아웃은 StackView의 축(axis), 분배(Distribution), 정렬방식(alignment), 여백(spacing)에 따라 배치된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"StackView의 내부 콘텐츠로 StackView를 resize할 수 있고, 반대로 StackView의 프로퍼티를 조정하므로써 내부 콘텐츠들의 크기를 조절할 수도 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"주의사항은 StackView의 position과 size(옵션)를 정의해줘야 한다. 그래야 StackView가 콘텐츠들의 레이아웃과 사이즈를 조정할 수 있다."}]}]}]}]},{"content":[{"level":3,"anchor":"%EC%BD%94%EB%93%9C%EB%A1%9C-Stack-View-%EA%B5%AC%ED%98%84%ED%95%B4%EB%B3%B4%EA%B8%B0","text":"코드로 Stack View 구현해보기","type":"heading"},{"type":"codeListing","syntax":"swift","code":["\/\/ 스택 뷰 생성","let stackView = UIStackView()","stackView.axis = .horizontal \/\/ 가로축","stackView.alignment = .fill","stackView.distribution = .fill","stackView.spacing = 5"]}]},{"content":[{"level":3,"type":"heading","anchor":"Scroll-View%EB%9E%80","text":"Scroll View란?"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"포함된 View의 scrolling하고 zooming을 허용하는 View이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"UIScrollView는 ","type":"text"},{"code":"UITableView","type":"codeVoice"},{"type":"text","text":", "},{"type":"codeVoice","code":"UITextView"},{"text":", ","type":"text"},{"type":"codeVoice","code":"UICollecctionView"},{"type":"text","text":"을 포함하여 몇가지 UIKit 클래스들의 superclass이다. 주로 사진 확대 축소할 때 많이 사용된다고 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스크롤 뷰는 스크롤되는 뷰들을 담고 있는 "},{"code":"Content Layout","type":"codeVoice"},{"text":"과 화면에 보여지는 ","type":"text"},{"code":"Frame Layout","type":"codeVoice"},{"type":"text","text":"으로 나뉘어 진다."}]}]},{"content":[{"type":"heading","text":"Content Layout Guide","level":3,"anchor":"Content-Layout-Guide"},{"type":"paragraph","inlineContent":[{"text":"들어오는 컨텐츠에 따라 레이아웃을 변경시켜줄테니 컨텐츠 크기를 나에게 알려줘","type":"text"}]}]},{"content":[{"anchor":"Frame-Layout-Guide","level":3,"text":"Frame Layout Guide","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"스크롤과 관계없이 화면 안에 고정되어야 할 요소가 있어? 나에게 위치를 알려줘"}]}]}]},{"code":["\/\/콘텐츠뷰의 크기","var contentSize: CGSize { get set }","","\/\/콘텐츠뷰의 원점이 스크롤뷰의 원점에서 오프셋 된 지점","var contentOffset: CGPoint { get set }","","\/\/스크롤뷰의 원점에 대한 콘텐츠뷰의 오프셋 설정.","func setContentOffset(_ contentOffset: CGPoint, animated: Bool)"],"type":"codeListing","syntax":"swift"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/WndyZXB.png"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Scroll View를 추가하고 제약조건을 추가해줘도 빨간 경고가 뜬다. 이유가 뭘까?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"그것은 바로 ‘Scroll View안에 들어갈 Content의 사이즈가 지정이 되지 않았다’ 라는 경고다. 따라서 Scroll View 안에 Content View를 추가해주고 제약을 준다면 해결된다","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Content Layout의 크기가 Label들의 크기에 맞게 늘어난 형태이다."}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/WKgK57b.gif"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Frame Layout Guide와 관계를 맺어 고정되어있는 Label","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/J1tfyNA.png"}]}]},{"content":[{"inlineContent":[{"text":"Scroll View와 Stack View를 활용한 예제를 만들어보았다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/N8jzWMc.png"}]},{"syntax":"swift","code":["@IBAction func addView() {"," let label = UILabel()"," label.font = UIFont.preferredFont(forTextStyle: .largeTitle)"," label.isHidden = true"," label.text = \"\"\""," dasdasd"," asdasdasd"," asda"," dasd"," asdasdsadas"," asdasdasdasd"," asdasdasdas"," asdasdasdasd"," \"\"\""," label.numberOfLines = 0"," label.adjustsFontForContentSizeCategory = true"," stackView.addArrangedSubview(label)"," "," UIView.animate(withDuration: 0.3) {"," label.isHidden = false"," }","}","","@IBAction func removeView() {"," guard let last = stackView.arrangedSubviews.last else { return }"," "," UIView.animate(withDuration: 0.3) {"," last.isHidden = true"," } completion: { _ in"," self.stackView.removeArrangedSubview(last)"," }","}"],"type":"codeListing"}]}],"type":"unorderedList"},{"type":"unorderedList","items":[{"content":[{"level":3,"type":"heading","text":"자동 스크롤 구현해보기","anchor":"%EC%9E%90%EB%8F%99-%EC%8A%A4%ED%81%AC%EB%A1%A4-%EA%B5%AC%ED%98%84%ED%95%B4%EB%B3%B4%EA%B8%B0"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"스크롤 뷰의 콘텐츠 뷰 원점을 설정하는 메소드를 활용해보았다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["let bottomOffset = CGPoint(x: 0, y: scrollView.contentSize.height - scrollView.bounds.height + scrollView.contentInset.bottom)","scrollView.setContentOffset(bottomOffset, animated: false)"]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"그러나 스크롤 바가 생길때 약간 움직이면서 스크롤이 안되는…[?] 멍청해보이는 애니메이션을 취하는 문제가 발생했다."}],"type":"paragraph"}]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/v4OYFOe.gif","type":"image"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 문제를 해결해보기 위해서 1차적으로 구글링을 통해 찾아보았고, 찾는데 어려움이 있어 3기 캠퍼 선배분들에게도 질문해보았지만 해결방법을 찾지 못했다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"3기 캠퍼 "},{"type":"codeVoice","code":"수박"},{"text":"의 도움으로 LLDB를 활용하여 처음 y좌표가 어떤 값으로 찍히는지 확인해보았으나, StackView가 추가되고 난 후 업데이트 되어 y값 좌표가 계산이 되어야 하는데, 버튼을 누르기 이전의 y좌표가 찍히는 것이 확인되었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"타이머나 비동기적으로 해당 부분을 해결해보려고 해보았으나 발만 담궈봤지 제대로 배워보지 않은 지식이라 해결하는데 많은 어려움이 있었다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이후 서포터즈 "},{"code":"Wody","type":"codeVoice"},{"text":"에게 질문해보았고 얼마 지나지 않아 해결을 할 수 있게 되었다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정상적으로 하단으로 자동 스크롤이 되는 모습"}]}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/uUW8GWB.gif"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"layoutIfNeeded()","type":"codeVoice"},{"type":"text","text":"라는 메소드를 활용하여 해결하게 되었다. ("},{"type":"codeVoice","code":"도움을 주신 갓wody에게 감사의 인사를 드립니다."},{"text":") 이 메소드는 ","type":"text"},{"code":"setNeedsLayout()","type":"codeVoice"},{"text":"과 같이 수동으로 layoutSubviews를 예약하는 행위이지만 해당 예약을 바로 실행시키는 동기적으로 작동하는 메소드다. update cycle이 올 때 까지 기다려 layoutSubviews를 호출시키는 것이 아니라 그 즉시 layoutSubviews를 발동시키는 메소드다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"만일 main run loop에서 하나의 View가 setNeedsLayout을 호출하고 그 다음 layoutIfNeeded를 호출한다면 layoutIfNeeded는 그 즉시 View의 값이 재계산되고 화면에 반영하기 때문에 setNeedsLayout이 예약한 layoutSubviews 메소드는 update cycle에서 반영해야할 변경된 값이 존재하지 않기 때문에 호출되지 않는다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"이러한 동작 원리로 ","type":"text"},{"type":"codeVoice","code":"layoutIfNeeded()"},{"type":"text","text":"는 그 즉시 값이 변경되어야 하는 애니메이션에서 많이 사용된다고 한다."},{"type":"text","text":" "},{"text":" ","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"text":"추가로 공부해보아야할 것","level":2,"anchor":"%EC%B6%94%EA%B0%80%EB%A1%9C-%EA%B3%B5%EB%B6%80%ED%95%B4%EB%B3%B4%EC%95%84%EC%95%BC%ED%95%A0-%EA%B2%83","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"setNeedsLayout()"}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Main Run Loop"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"Update Cycle","type":"codeVoice"}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"UIView methods"},{"text":" ","type":"text"},{"text":" ","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uistackview","isActive":true,"type":"reference"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uiscrollview"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/baked-corn.tistory.com\/105","isActive":true,"type":"reference"}],"type":"paragraph"}]}]}]}]}]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"sections":[],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211116_scroll_view,_stack_view,_setcontentoffset,_layoutifneeded,_setneedslayout"]}],"kind":"article","references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/N8jzWMc.png":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/N8jzWMc.png","variants":[{"url":"https:\/\/i.imgur.com\/N8jzWMc.png","traits":["1x","light"]}]},"https://baked-corn.tistory.com/105":{"type":"link","url":"https:\/\/baked-corn.tistory.com\/105","identifier":"https:\/\/baked-corn.tistory.com\/105","title":"setNeedsLayout vs layoutIfNeeded","titleInlineContent":[{"type":"text","text":"setNeedsLayout vs layoutIfNeeded"}]},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://developer.apple.com/documentation/uikit/uiscrollview":{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uiscrollview","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uiscrollview","type":"link","titleInlineContent":[{"type":"text","text":"UIScrollView"}],"title":"UIScrollView"},"https://developer.apple.com/documentation/uikit/uistackview":{"type":"link","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uistackview","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uistackview","title":"UIStackView","titleInlineContent":[{"type":"text","text":"UIStackView"}]},"https://i.imgur.com/WKgK57b.gif":{"variants":[{"url":"https:\/\/i.imgur.com\/WKgK57b.gif","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/i.imgur.com\/WKgK57b.gif","alt":null},"https://i.imgur.com/uUW8GWB.gif":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/uUW8GWB.gif","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/uUW8GWB.gif"}]},"https://i.imgur.com/UztL3Aj.png":{"alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/UztL3Aj.png"}],"identifier":"https:\/\/i.imgur.com\/UztL3Aj.png"},"https://i.imgur.com/v4OYFOe.gif":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/v4OYFOe.gif","variants":[{"url":"https:\/\/i.imgur.com\/v4OYFOe.gif","traits":["1x","light"]}]},"https://i.imgur.com/J1tfyNA.png":{"alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/J1tfyNA.png"}],"identifier":"https:\/\/i.imgur.com\/J1tfyNA.png"},"https://i.imgur.com/WndyZXB.png":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/WndyZXB.png","variants":[{"url":"https:\/\/i.imgur.com\/WndyZXB.png","traits":["1x","light"]}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211118_functional_programming,_higher-order_function,_main_run_loop,_update_cycle,_uiview,_attribute.json b/docs/data/documentation/arinote/211118_functional_programming,_higher-order_function,_main_run_loop,_update_cycle,_uiview,_attribute.json new file mode 100644 index 0000000..eed4bfb --- /dev/null +++ b/docs/data/documentation/arinote/211118_functional_programming,_higher-order_function,_main_run_loop,_update_cycle,_uiview,_attribute.json @@ -0,0 +1 @@ +{"kind":"article","sections":[],"metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"title":"211118 Functional Programming, Higher-order function, Main Run Loop, Update Cycle, UIView, attribute","roleHeading":"Article"},"schemaVersion":{"patch":0,"major":0,"minor":3},"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","level":1,"type":"heading","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"text":"11월 18일 (목)","type":"text"}]},{"type":"heading","text":"학습 내용","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"함수형 프로그래밍, 고차함수 활동학습"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Main Run Loop"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Update Cycle"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIView methods"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"attribute"},{"type":"text","text":" "},{"text":" ","type":"text"}],"type":"paragraph"}]}]},{"type":"heading","anchor":"%EA%B3%B5%EB%B6%80%EB%82%B4%EC%9A%A9-%EC%A0%95%EB%A6%AC","text":"공부내용 정리","level":2},{"type":"heading","text":"함수형 프로그래밍이란?","anchor":"%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%B4%EB%9E%80","level":1},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"모든 것을 순수 함수로 나누어 문제를 해결하는 기법"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"작은 문제를 해결하기 위한 함수를 작성하여 가독성을 높이고 유지보수를 용이하게 해준다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"클린 코드(Clean Code)의 저자 Robert C.Martin은 함수형 프로그래밍을 대입문이 없는 프로그래밍이라고 정의하였다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉 대입문을 사용하지 않는 프로그래밍이며, 작은 문제를 해결하기 위한 함수를 작성한다."}]}]}]},{"type":"heading","anchor":"%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%98-%ED%8A%B9%EC%A7%95","level":1,"text":"함수형 프로그래밍의 특징"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"부수 효과가 없는 순수 함수를 1급 객체로 간주하여 파라미터로 넘기거나 반환값으로 사용할 수 있으며, 참조 투명성을 지킬 수 있다."}]}]}],"type":"unorderedList"},{"level":1,"anchor":"%EB%B6%80%EC%88%98-%ED%9A%A8%EA%B3%BCSide-Effect%EB%9E%80","text":"부수 효과(Side-Effect)란?","type":"heading"},{"inlineContent":[{"type":"text","text":"다음과 같은 변화 또는 변화가 발생하는 작업을 의미한다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"변수의 값이 변경됨"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"자료 구조를 제자리에서 수정함","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"객체의 필드값을 설정함","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"예외나 오류가 발생하며 실행이 중단됨","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"콘솔 또는 파일 I\/O가 발생함","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"anchor":"%EC%88%9C%EC%88%98-%ED%95%A8%EC%88%98%EB%9E%80","level":1,"text":"순수 함수란?","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Memory or I\/O의 관점에서 Side Effect가 없는 함수"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"함수의 실행이 외부에 영향을 끼치지 않는 함수"}]}]}],"type":"unorderedList"},{"text":"순수 함수로 얻을 수 있는 효과","anchor":"%EC%88%9C%EC%88%98-%ED%95%A8%EC%88%98%EB%A1%9C-%EC%96%BB%EC%9D%84-%EC%88%98-%EC%9E%88%EB%8A%94-%ED%9A%A8%EA%B3%BC","level":1,"type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"함수 자체가 독립적이며 Side-Effect가 없기 때문에 Thread에 안전성을 보장받을 수 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Thread에 안전성을 보장받아 병렬 처리를 동기화 없이 진행할 수 있다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"anchor":"%EC%B0%B8%EC%A1%B0-%ED%88%AC%EB%AA%85%EC%84%B1Referential-Transparency%EC%9D%B4%EB%9E%80","text":"참조 투명성(Referential Transparency)이란?","level":1,"type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"동일한 인자에 대해 항상 동일한 결과를 반환해야 한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"참조 투명성을 통해 기존의 값은 변경되지 않고 유지된다. (Immutable Data)","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"level":1,"anchor":"Swift%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%98%88%EC%8B%9C","text":"Swift를 이용한 함수형 프로그래밍 예시","type":"heading"},{"items":[{"content":[{"inlineContent":[{"text":"함수를 모두 처리하고 싶을 경우","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"text":"명령형 프로그래밍의 예","level":2,"anchor":"%EB%AA%85%EB%A0%B9%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%98-%EC%98%88","type":"heading"},{"syntax":"swift","type":"codeListing","code":["class CommandProgramming {"," \/\/명령형 프로그래밍"," "," func doSomething(){"," print(\"doSomething\")"," }"," "," func doAnotherThing(){"," print(\"doAnotherThing\")"," }"," "," func executeAll(){"," doSomething()"," doAnotherThing()"," }","}","","\/\/호출부","executeAll()"]},{"text":"함수형 프로그래밍의 예","level":2,"anchor":"%ED%95%A8%EC%88%98%ED%98%95-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%98-%EC%98%88","type":"heading"},{"syntax":"swift","type":"codeListing","code":["class MethodProgramming {"," func doSomething(){"," print(\"do something\")"," }"," "," func doAnotherThing(){"," print(\"do another thing\")"," }"," "," func execute(tasks:[() -> Void]){"," for task in tasks {"," task()"," }"," }"," ","}","","\/\/호출부","execute(tasks: [doAnotherThing, doSomething])"]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"함수가 일급 시민이므로 함수를 전달인자 또는 반환값으로 사용할 수 있다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그래서 execute(tasks: [doAnotherThing, doSomething]) 처럼 함수를 전달인자로 사용할 수 있는 것이다."}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"anchor":"Main-Run-Loop","level":1,"text":"Main Run Loop","type":"heading"},{"inlineContent":[{"text":"어플리케이션이 실행되면 iOS의 UIApplication이 메인 스레드에서 main run loop를 실행 시킨다. main run loop는 돌아가면서 터치 이벤트, 위치의 변화, 디바이스의 회전 등의 각종 이벤트들을 처리하게 된다. 이러한 처리 과정은 각 이벤트들에 알맞는 핸들러를 찾아 그들에게 권한을 위임하며 진행된다.","type":"text"}],"type":"paragraph"},{"name":"Note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"text":"버튼의 터치 이벤트를 @IBAction 메소드가 처리하는 것과 같다.","type":"text"}]}],"style":"note"},{"type":"paragraph","inlineContent":[{"type":"text","text":"이렇게 발생한 이벤트들을 모두 처리하고 권한이 다시 main run loop로 돌아오게 되고 이 시점을 update cycle이라고 한다."}]},{"type":"heading","text":"Update Cycle","level":1,"anchor":"Update-Cycle"},{"type":"paragraph","inlineContent":[{"text":"main run loop에서 이벤트가 처리되는 과정에서 버튼을 누르면 크기나 위치가 이동하는 애니메이션과 같이 layout이나 position 값을 바꾸는 핸들러가 실행될 때도 있다. 이러한 변화는 즉각적으로 반영되는 것이 아니다.","type":"text"},{"type":"text","text":" "},{"text":"시스템은 이러한 layout이나 position이 변화되는 View를 체크한다. 그리고 모든 핸들러가 종료되고 main run loop로 권한이 다시 돌아오는 시점인 update cycle에서 이런 view들의 값을 바꿔주어 position이나 layout의 변화를 적용 시킨다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"text":"즉 postion이나 layout값을 변경하는 코드와 실제로 변경되는 값이 반영되는 시점에는 시간차가 존재한다는 뜻이다.","type":"text"}]},{"inlineContent":[{"text":"이러한 시간차가 존재한다는 것을 알고 있어야 setNeedsLayout과 layoutIfNeeded의 차이를 알 수 있다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/v8FsiHM.png","type":"image"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"이렇게 시간차가 존재하지만 이 시간차는 사용자가 체감할 수 없을 정도로 짧기 때문에 사용자는 그러한 시간차를 느끼지 못한다. 하지만 개발하는 사람은 이러한 시간차를 인지하고 있어야 정확히 원하는 핸들러를 구현할 수 있다."}]},{"text":"UIView methods","type":"heading","level":1,"anchor":"UIView-methods"},{"type":"paragraph","inlineContent":[{"type":"text","text":"UIView에는 여러 내장 메소드가 존재하는데 중요한 메소드를 알아보자."}]},{"text":"layoutSubviews()","level":2,"type":"heading","anchor":"layoutSubviews"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"View의 값을 호출한 즉시 변경시켜주는 메소드","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"호출되면 해당 View의 모든 Subview들의 layoutSubviews() 또한 연달아 호출한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그렇기 때문에 비용이 많이 드는 메소드이고 그렇기 때문에 직접 호출하는 것은 지양한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"이는 시스템에 의해서 View의 값이 재계산되어야 하는 적절한 시점(update cycle)에 자동으로 호출된다."}],"type":"paragraph"}]}]},{"inlineContent":[{"text":"그렇기 때문에 layoutSubviews를 유도할 수 있는 여러 방법이 존재한다. 이는 일종의 update cycle에서 layoutSubviews의 호출을 예약하는 행위라고 할 수 있다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"UIViewController내의 View가 재계산되어 다시 그려지는 행위가 발생하면, 즉 layoutSubViews가 호출 되고 View의 값이 갱신되고 나면 뒤이어 UIViewController의 메소드인 viewDidLayoutSubViews가 호출된다. 그렇기 때문에 갱신된 View 값에 의존하는 행위들은 viewDidLayoutSubViews에 명시를 해주어야 한다."}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"예를 들어 Layer값은 자동으로 변경되지 않기 때문에 속한 View의 frame이 변경되면 viewDidLayoutSubViews안에 Layer의 frame을 변경하는 코드를 작성해주어야 한다.","type":"text"}]}],"type":"aside","style":"note","name":"Note"},{"type":"paragraph","inlineContent":[{"type":"text","text":"layoutSubviews를 update cycle에서 호출되게끔 자동으로 예약 해주는 상황들이 몇가지 존재한다. 즉 다음 상황에서는 시스템이 자동으로 size와 position이 변경되어야 하는 View라고 체크를 하고 updatecycle에서는 layoutSubviews가 호출되어 체크된 View의 layer와 position에 변경된 값을 반영한다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"View의 크기를 조절할 때"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Subview를 추가할 때"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자가 UIScrollView를 스크롤할 때"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"디바이스를 회전시켰을 때(Portrait, Landscape)","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"View의 Auto Layout contraint 값을 변경시켰을 때","type":"text"},{"type":"text","text":" "},{"type":"text","text":"위에 나열된 시점에는 자동으로 update cycle에서 layoutSubviews를 호출하는 행위를 예약하는 것이다. 하지만 이렇게 자동으로 예약하는 행위 이외에도 수동으로 예약할 수 있는 메소드도 존재한다."}]}]}],"type":"unorderedList"},{"text":"setNeedsLayout()","type":"heading","level":2,"anchor":"setNeedsLayout"},{"inlineContent":[{"text":"layoutSubviews를 예약하는 행위 중 가장 비용이 적게 드는 방법이 setNeedsLayout을 호출하는 것이다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 메소드를 호출한 View는 재계산되어야 하는 View라고 수동으로 체크가 된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"update cycle에서 layoutSubviews가 호출되게 된다."}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":"이 메소드는 비동기적으로 작동하기 때문에 호출되고 바로 반환된다. 그리고 View의 보여지는 모습은 update cycle에 들어갔을 때 바뀌게 된다.","type":"text"}]},{"type":"heading","level":2,"anchor":"layoutIfNeeded","text":"layoutIfNeeded()"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이 메소드는 setNeedsLayout과 같이 수동으로 layoutSubviews를 예약하는 행위이지만 해당 예약을 바로 실행시키는 동기적으로 작동하는 메소드다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Update cycle이 올 때까지 기다려 layoutSubviews를 호출시키는 것이 아니라 그 즉시 layoutSubviews를 발동시키는 메소드다.","type":"text"}],"type":"paragraph"}]}]},{"inlineContent":[{"text":"만일 main run loop에서 하나의 View가 setNeedsLayout을 호출하고 그 다음 layoutIfNeeded를 호출한다면 그 즉시 View의 값이 재계산되고 화면에 반영하기 때문에 setNeedsLayout이 예약한 layoutSubviews 메소드는 update cycle에서 반영해야할 변경된 값이 존재하지 않기 때문에 호출되지 않는다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"이러한 동작 원리로 layoutIfNeeded는 그 즉시 값이 변경되어야 하는 애니메이션에서 많이 사용된다. 만일 setNeedsLayout을 사용한다면 애니메이션 블록에서 그 즉시 View의 값이 변경되는 것이 아니라 추후 update cycle에서 값이 반영되므로 값의 변경은 이루어지지만 애니메이션 효과는 볼 수 없는 것이다.","type":"text"},{"type":"text","text":" "},{"text":"setNeedsLayout과 layoutIFNeeded의 차이점은 동기적으로 동작하느냐 비동기적으로 동작하느냐의 차이다.","type":"text"}]},{"type":"thematicBreak"},{"level":1,"anchor":"attribute","type":"heading","text":"attribute"},{"inlineContent":[{"text":"attribute는 선언 또는 타입등에 대한 부가 정보를 나타낸다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"스위프트에는 세가지 종류의 attribute가 있는데 선언에 부여하는 attribute, 타입에 부여하는 attribute, 스위치 케이스에 부여하는 attribute이 있다."},{"type":"text","text":" "},{"type":"text","text":"attribute은 @ 표시를 속성 이름 앞에 명시한다."}],"type":"paragraph"},{"code":["@속성 이름","@속성 이름(파라미터)"],"syntax":"swift","type":"codeListing"},{"anchor":"%EC%84%A0%EC%96%B8%EC%86%8D%EC%84%B1","level":2,"type":"heading","text":"선언속성"},{"type":"paragraph","inlineContent":[{"type":"text","text":"선언에만 사용할 수 있다"},{"type":"text","text":" "},{"type":"text","text":"(클래스의 선언, 함수의 선언, 열거형의 선언, 프로토콜의 선언)"}]},{"type":"heading","text":"available","level":2,"anchor":"available"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"특정 플랫폼 또는 운영체제의 버전에 관련된 속성이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"available 속성은 매개변수가 두개 이상 나열되는 리스트다."}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":"매개변수로 사용할 수 있는 플랫폼 이름","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"iOS (iOSApplicationExtension)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"macOS (macOSApplicationExtension)","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"watchOS (watchOSApplicationExtension)","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"tvOS (tvOSApplicationExtension)","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"swift","type":"text"},{"type":"text","text":" "},{"text":"모든 플랫폼에 적용할 수 있도록 하려면 리스트에 애스터리스크(*)를 적어주면 된다.","type":"text"}]}]}]},{"inlineContent":[{"type":"text","text":"나머지 매개변수는 추가정보를 나타내는데 쓰인다."},{"type":"text","text":" "},{"text":"중요한 남김말이나 생명주기 등의 자세한 정보를 나타낼 수 있다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"unavailable, introduced, deprecated, obsoleted, message, renamed"}]}]}]},{"level":2,"text":"unavailable","anchor":"unavailable","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 플랫폼에서 사용할 수 없는 선언임을 나타낸다."}]},{"code":["@available(tvOS, unavailable)","class SomeClass {}","\/\/ tvOS에서 사용할 수 없는 클래스"],"type":"codeListing","syntax":"swift"},{"text":"introduced","level":2,"type":"heading","anchor":"introduced"},{"type":"paragraph","inlineContent":[{"text":"이 선언이 어떤 버전에서 처음으로 소개(작성)되었는지 나타낸다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"콜론(:) 뒤에 버전 번호를 덧붙여준다. 버전 번호는 양수로 나타낸다."}]},{"syntax":"swift","type":"codeListing","code":["class SomeClass {"," \/\/ 이 프로퍼티는 스위프트 4.0에서 작성했으므로"," \/\/ 스위프트 4.0 이상에서만 사용할 수 있다."," @available(swift, introduced: 4.0)"," var multilineString: String {"," return \"\"\""," 여러줄"," 문자열은"," 스위프트 4.0 이상에서"," 사용할 수 있습니다."," \"\"\""," }","}"]},{"level":2,"type":"heading","anchor":"deprecated","text":"deprecated"},{"inlineContent":[{"type":"text","text":"이 선언이 어떤 버전에서 사용이 제한(중지)되었는지 나타낸다."},{"type":"text","text":" "},{"type":"text","text":"특별히 버전을 명시하고 싶지 않다면 뒤에 콜론과 버전을 생략해도 된다."}],"type":"paragraph"},{"syntax":"swift","code":["@available(*, deprecated: 2.0.0)","class SomeClass {}","\/\/ 이 클래스는 2.0.0 버전부터 사용이 제한되었습니다."],"type":"codeListing"},{"text":"obsoleted","type":"heading","level":2,"anchor":"obsoleted"},{"type":"paragraph","inlineContent":[{"type":"text","text":"이 선언이 어떤 버전부터 버려진 것인지 나타낸다. 버려진 선언은 더이상 사용할 수 없다."}]},{"syntax":"swift","type":"codeListing","code":["@available(*, obsoleted: 2.0.0)","class SomeClass {}","\/\/ 이 클래스는 2.0.0 버전부터 사용이 불가능합니다."]},{"level":2,"type":"heading","anchor":"message","text":"message"},{"inlineContent":[{"type":"text","text":"사용이 제한되거나 불가능한 선언을 사용하려고 할 때 컴파일러로 프로그래머에게 전달할 경고 또는 오류 메세지다."},{"text":" ","type":"text"},{"type":"text","text":"메세지는 문자열 리터럴로 작성한다."}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["@available(*, deprecated: 2.0.0, message: \"아마도 쓰지 않는 것이 좋을 걸?\")","class SomeClass {}","\/\/ 이 클래스는 2.0.0 버전부터 사용이 제한되었습니다.","","Let instance = SomeClass() \/\/ 플랫폼 버전이 기준 버전보다 높다면 컴파일러 경고","\/\/ ‘SomeClass’ is deprecated: 아마도 쓰지 않는 것이 좋을 걸?"]},{"text":"renamed","type":"heading","anchor":"renamed","level":2},{"inlineContent":[{"type":"text","text":"해당 선언이 교체되어 다른 이름으로 변경되었을 때 그 다른 이름을 나타낸다."},{"type":"text","text":" "},{"type":"text","text":"다른 이름으로 교체된 선언을 사용하려 할 때 컴파일러를 통해 프로그래머에게 다른 클래스를 사용할 것을 제안할 경고 또는 오류 메세지다. 메세지는 문자열 리터럴로 작성한다."}],"type":"paragraph"},{"syntax":"swift","type":"codeListing","code":["@available(*, deprecated: 2.0.0, message: \"아마도 쓰지 않는 것이 좋을 걸?\", renamed: \"NewClass\")","class SomeClass {}","\/\/ 이 클래스는 2.0.0 버전부터 사용이 제한되었습니다.","","@available(*, unavailable, message: \"사용 불가\", renamed: \"NewClass\")","class AnotherClass {}","\/\/ 이 클래스는 사용 불가능합니다.","","@available(*, introduced: 2.0.0)","class NewClass {}","","let someInstance = SomeClass() \/\/ 플랫폼 기준 버전보다 높다면 컴파일러 경고","let anotherInstance = AnotherClass() \/\/ 컴파일러 오류","let newInstance = NewClass() \/\/ 플랫폼 버전이 기준 버전보다 낮자면 컴파일러 오류"]},{"type":"paragraph","inlineContent":[{"type":"text","text":"available 속성의 매개변수로 여러 플랫폼과 여러 매개변수를 동시에 전달할 수 있다."},{"text":" ","type":"text"},{"type":"text","text":"introduced 매개변수를 생략하고 곧바로 버전을 명시해줄 수도 있다."},{"type":"text","text":" "},{"type":"text","text":"몇가지 플랫폼에서 사용할 것을 한정할 때는 사용하고 싶은 플랫폼 이름과 최소 버전을 명시해 주면 된다."},{"type":"text","text":" "},{"text":"그리고 리스트의 맨 마지막은 꼭 애스터리스크(*)로 끝나야 한다.","type":"text"}]},{"type":"codeListing","code":["\/\/ 예시","@available(iOS 11.0, *)","func someFunction() { }","","@available(iOS 11.0, macOS 10.13, watchOS 4.0, *)","func anotherFunction() { }"],"syntax":"swift"},{"anchor":"discardableResult","type":"heading","level":1,"text":"discardableResult"},{"inlineContent":[{"type":"text","text":"반환 값을 유의미하게 사용하지 않고 버려도 되는 함수들이 있을 때가 있는데, 이때 컴파일러 경고가 발생하지 않도록 하려면 discardableResult 속성을 함수나 메서드의 정의에 부여하면 된다."}],"type":"paragraph"},{"syntax":"swift","code":["@discardableResult","Func someFunction() { }"],"type":"codeListing"},{"text":"objc","anchor":"objc","level":1,"type":"heading"},{"inlineContent":[{"type":"text","text":"스위프트로 선언된 코드를 Objective-C의 코드에서 사용할 수 있도록 하려면 objc 속성을 사용한다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"중첩 타입, 제네릭 열거형 등은 objc 속성을 사용할 수 없다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"objc 속성이 부여된 클래스는 Objective-C의 클래스를 꼭 상속받아야 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"objc 속성이 부여된 클래스를 상속받는 클래스는 암시적으로 objc 속성이 부여된다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"objc 속성이 부여된 프로토콜은 objc 속성이 부여되지 않은 스위프트의 프로토콜을 상속받을 수 없다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"objc 속성이 부여된 프로토콜을 상속받는 프로톸ㄹ은 암시적으로 objc 속성이 부여된다."}]}]}]},{"code":["@objc","class SomeClass {}","\/\/ 오류 - Objective-C의 클래스를 상속받지 않았습니다.","","\/\/ Objective-C의 클래스를 상속받으면 암시적으로 @objc 속성이 부여됩니다.","Class AnotherClass: NSObject { }","","protocol ParentProtocol {}","","@objc","Protocol SomeProtocol: ParentProtocol { }","\/\/ 오류 - ParentProtocol은 objc 속성이 없습니다."],"type":"codeListing","syntax":"swift"},{"inlineContent":[{"text":"objc 속성이 부여된 열거형은 Objective-C 코드에서 사용할 수 있다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":"다만 원시값 타입을 Int로 지정해주어야 한다. 스위프트로 작성한 각 열거형 case의 이름은 Objective-C 코드에서 Objective-C 스타일의 열거형 case 이름으로 나타낸다. 예를들어 스위프트의 Planet 열거형의 earth 케이스는 Objective-C 코드에서 PlanetEarth라는 이름으로 나타낸다."}],"type":"paragraph"},{"type":"codeListing","code":["@objc","enum Planet: Int {"," case mercury, venus, earth, mars, jupiter"," \/\/ Objective-C 이름"," \/\/ PlanetMercury, PlanetVenus, PlanetEarth, PlanetMars. PlanetJupiter","}"],"syntax":"swift"},{"type":"paragraph","inlineContent":[{"text":"스위프트와 Objective-C의 명명 규칙이 다르기 때문에 주의해야 한다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":"자동으로 변경되지 않는 이름은 수동으로 이름을 정해줘야 할 때도 있다."},{"type":"text","text":" "},{"text":"objc 속성을 부여할 때 매개변수로 이름을 전달하면 해당 선언이 Objective-C 코드에서는 매개변수로 전달한 이름으로 사용한다.","type":"text"},{"type":"text","text":" "},{"text":"다만 이름을 변경할 수 있는 선언은 클래스, 열거형, 열거형 case, 프로토콜, 메서드, 접근자, 설정자, 이니셜라이저 등이 있다.","type":"text"}]},{"code":["@objc(Example)"," \/\/ Objective-C 코드에서는 ExampleClass 클래스의 이름이 Example이라고 보인다.","class ExampleClass: NSObject {"," @objc var enabled: Bool {"," \/\/ Objective-C 코드에서는 enabled 프로퍼티 이름이 isEnabled: 라고 보인다"," @objc(isEnabled) get {"," return true"," }"," }",""," \/\/ Objective-C 코드에서는 print(name:) 메서드의 이름이 printWithName: 이라고 보인다."," @objc(printWithName:)"," func print(name: String) { }",""," \/\/ Objective-C 코드에서는 init(name:) 메서드의 이름이 initWithName: 이라고 보인다."," @objc(initWithName:)"," init(name: String) { }","}"],"type":"codeListing","syntax":"swift"},{"type":"heading","anchor":"nbnobjc","text":"nbnobjc","level":1},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Objective-C 코드에서는 사용이 불가능하다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"objc 속성이 부여된 메서드로 재정의할 수 없다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"objc 속성이 요구하는 프로토콜 요구사항을 충족할 수 없다."}]}]}]},{"anchor":"testable","level":1,"type":"heading","text":"testable"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"테스트를 위해 컴파일한 모듈에 정의한 내부 접근수준 정의를 공개 접근수준로 정의한 것 처럼 만들어준다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"외부에서 가져다 테스트할 수 있도록 부여할 수 있는 속성이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테스트를 위한 코드는 공개 접근수준과 내부 접근수준으로 testable 속성과 함께 정의한 클래스나 클래스 요소에 개방 접근수준의 접근수준처럼 접근하여 테스트할 수 있다."}]}]}]},{"text":"objcMembers","anchor":"objcMembers","type":"heading","level":1},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Objc 속성을 부여할 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정의 하나만 Objective-C 노출하려는 경우에 주로 사용한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"많은 양의 정의를 노출하고자 한다면 익스첸션에 그 정의를 묶어 objc 속성을 주는 것이 좋다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"불필요한 곳에 objc 속성을 남용하면 바이너리크기가 커질 뿐만 아니라 성능에 영향을 미칠 수도 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"클래스나 라이브러리 크기가 클 때 모든 멤버가 Objective-C 멤버로 노출될 필요가 없다면 objcMembers를 사용한다."}]}]}]},{"text":"dynamicMemberLookup","type":"heading","level":1,"anchor":"dynamicMemberLookup"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"실행중(Runtime)에 이름으로 멤버(프로퍼티 등)를 찾을 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 속성을 적용한 타입은 subscript(dynamicMemberLookup:) 서브스크립트를 정의해야한다."}]}]},{"content":[{"inlineContent":[{"text":"이 속성을 적용한 타입의 인스턴스에 정확한 멤버 표현(예를 들어 점 표기법 등)에서 적절한 멤버를 찾지 못하면 서브스크립트에 매개변수로 그 멤버의 이름이 전달한 subscript(dynamicMemberLookup:) 서브스크립트가 호출된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"서브스크립트의 매개변수는 키경로 혹은 멤버 이름을 모두 수용할 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 속성을 적용한 클래스를 상속받으면 상속받은 클래스도 dynamicMemberLookup 속성이 적용된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"KeyPath, ReferenceWritableKeyPath 등의 타입을 매개변수로 전달받을 수 있다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"멤버 이름을 ExpressibleByStringLiteral 프로토콜을 준수하는 타입의 매개변수로 전달받을 수도 있다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"멤버의 이름으로 멤버를 찾는 것은 실행중에 데이터가 존재할지 확신할 수 없는 상황에서 사용하기 좋다."}]}]}],"type":"unorderedList"},{"type":"codeListing","code":["@dynamicMemberLookup","Struct Contacts {"," private let contacts: [String: String] = [\"Ari\" : \"010-2222-3333\","," \"Ara\": \"010-4444-55555\"]"," subscript(dynamicMemberLookup member: String) -> String {"," return contacts[member] ?? \"114\""," }","}","Let contacts = Contacts()","","\/\/ Dynamic Member Lookup 사용","let aris = contacts.Ari","","\/\/ 서브스크립트 직접 호출","let aras = contacts[dynamicMember: \"Ara\"]","","\/\/ 찾을 수 없는 경우","let somebody = contacts.somebody","let anybody = contacts[dynamicMember: \"anybody\"]"],"syntax":"swift"},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/mangkyu.tistory.com\/111","isActive":true}]}]},{"content":[{"inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/borabong.tistory.com\/5"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/tech.gc.com\/demystifying-ios-layout\/","type":"reference","isActive":true}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","isActive":true,"identifier":"https:\/\/developer.apple.com\/library\/archive\/documentation\/General\/Conceptual\/Devpedia-CocoaApp\/MainEventLoop.html"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/abhimuralidharan.medium.com\/ios-swift-setneedslayout-vs-layoutifneeded-vs-layoutsubviews-5a2b486da31c","isActive":true,"type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/baked-corn.tistory.com\/105","isActive":true}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스위프트 프로그래밍 3판 "},{"code":"548p","type":"codeVoice"},{"type":"text","text":" - "},{"code":"564p","type":"codeVoice"}]}]}]}]}],"type":"unorderedList"}]}],"variants":[{"paths":["\/documentation\/arinote\/211118_functional_programming,_higher-order_function,_main_run_loop,_update_cycle,_uiview,_attribute"],"traits":[{"interfaceLanguage":"swift"}]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211118_Functional_Programming,_Higher-order_function,_Main_Run_Loop,_Update_Cycle,_UIView,_attribute","interfaceLanguage":"swift"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"references":{"https://tech.gc.com/demystifying-ios-layout/":{"type":"link","identifier":"https:\/\/tech.gc.com\/demystifying-ios-layout\/","url":"https:\/\/tech.gc.com\/demystifying-ios-layout\/","title":"Demystifying iOS Layout","titleInlineContent":[{"text":"Demystifying iOS Layout","type":"text"}]},"https://borabong.tistory.com/5":{"type":"link","identifier":"https:\/\/borabong.tistory.com\/5","url":"https:\/\/borabong.tistory.com\/5","title":"함수형 프로그래밍이란? - 2","titleInlineContent":[{"type":"text","text":"함수형 프로그래밍이란? - 2"}]},"https://developer.apple.com/library/archive/documentation/General/Conceptual/Devpedia-CocoaApp/MainEventLoop.html":{"type":"link","identifier":"https:\/\/developer.apple.com\/library\/archive\/documentation\/General\/Conceptual\/Devpedia-CocoaApp\/MainEventLoop.html","url":"https:\/\/developer.apple.com\/library\/archive\/documentation\/General\/Conceptual\/Devpedia-CocoaApp\/MainEventLoop.html","title":"Main event Loop","titleInlineContent":[{"text":"Main event Loop","type":"text"}]},"https://abhimuralidharan.medium.com/ios-swift-setneedslayout-vs-layoutifneeded-vs-layoutsubviews-5a2b486da31c":{"type":"link","identifier":"https:\/\/abhimuralidharan.medium.com\/ios-swift-setneedslayout-vs-layoutifneeded-vs-layoutsubviews-5a2b486da31c","url":"https:\/\/abhimuralidharan.medium.com\/ios-swift-setneedslayout-vs-layoutifneeded-vs-layoutsubviews-5a2b486da31c","title":"iOS swift — setNeedsLayout vs layoutIfNeeded vs layoutSubviews()","titleInlineContent":[{"type":"text","text":"iOS swift — setNeedsLayout vs layoutIfNeeded vs layoutSubviews()"}]},"https://i.imgur.com/v8FsiHM.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/v8FsiHM.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/v8FsiHM.png"}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://mangkyu.tistory.com/111":{"type":"link","identifier":"https:\/\/mangkyu.tistory.com\/111","url":"https:\/\/mangkyu.tistory.com\/111","title":"함수형 프로그래밍이란? - 1","titleInlineContent":[{"text":"함수형 프로그래밍이란? - 1","type":"text"}]},"https://baked-corn.tistory.com/105":{"type":"link","identifier":"https:\/\/baked-corn.tistory.com\/105","url":"https:\/\/baked-corn.tistory.com\/105","title":"[ios] setNeedsLayout vs layoutIfNeeded","titleInlineContent":[{"type":"text","text":"[ios] setNeedsLayout vs layoutIfNeeded"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211119_viewdidload,_localizederror,_scrollview_bar_hide,_addsubview,_ibinspectable,_cornerradius,_viewwilllayoutsubviews,_private_extension.json b/docs/data/documentation/arinote/211119_viewdidload,_localizederror,_scrollview_bar_hide,_addsubview,_ibinspectable,_cornerradius,_viewwilllayoutsubviews,_private_extension.json new file mode 100644 index 0000000..e1f265a --- /dev/null +++ b/docs/data/documentation/arinote/211119_viewdidload,_localizederror,_scrollview_bar_hide,_addsubview,_ibinspectable,_cornerradius,_viewwilllayoutsubviews,_private_extension.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"content":[{"anchor":"TIL-Today-I-Learned","level":1,"text":"TIL (Today I Learned)","type":"heading"},{"inlineContent":[{"text":"11월 19일 (금)","type":"text"}],"type":"paragraph"},{"type":"heading","text":"학습 내용","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"계산기 프로젝트 STEP 3 피드백 반영"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"viewDidLoad()","type":"codeVoice"},{"type":"text","text":" 내부에 super는 꼭 호출해야할까?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"protocol LocalizedError"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"ScrollView에서 스크롤바 없애기"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"인터페이스 빌더에 속성을 추가하는 방법"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"둥근 버튼 만드는 방법"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"func viewWillLayoutSubviews()","type":"codeVoice"}]}]},{"content":[{"inlineContent":[{"text":"private extension?","type":"text"},{"type":"text","text":" "},{"type":"text","text":" "}],"type":"paragraph"}]}],"type":"unorderedList"},{"level":2,"type":"heading","text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"unorderedList","items":[{"content":[{"type":"heading","level":3,"anchor":"viewDidLoad-%EB%82%B4%EB%B6%80%EC%97%90-super%EB%8A%94-%EA%BC%AD-%ED%98%B8%EC%B6%9C%ED%95%B4%EC%95%BC%ED%95%A0%EA%B9%8C","text":"`viewDidLoad()` 내부에 super는 꼭 호출해야할까?"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"사실 그동안은 기본적으로 super를 호출하게끔 구현이 되어있어서 넘어갔던 사실인데, 엘림이 해당 호출이 꼭 필요한지 물어봐서 알아보게 되었다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"슈퍼클래스에 이미 선언되어있는 함수라 만든 클래스가 자동적으로 물려받아 코드를 쓰지 않아도 실행에는 문제가 없다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"하지만 viewDidLoad 메서드가 실행될 때 하고싶은 동작이 있다면 viewDidLoad 메서드 내부에 써주어야 하는데, 이미 슈퍼클래스에 선언되어져 있는 메서드를 하위클래스에서 사용하려면 override 전치자를 써주어야 한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"super라는게 어쨌건 부모 클래스의 메소드를 사용하겠다는 뜻이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"그래서 super.viewDidLoad()를 해줘서 어떤 기초적인 설정들을 ","type":"text"},{"code":"마무리","type":"codeVoice"},{"type":"text","text":"해주는 작업을 하고 그 이후에 우리가 하고싶은 작업들을 하면 된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"만약에 super.viewDidLoad()를 적지 않고 어떤 작업들을 해주게 된다면 아직 viewDidLoad 설정이 끝나지 않은 상태에서 어떤 작업들이 이루어지므로 이상한 버그와 동작이 발생할 수 있다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"정리하자면 viewDidLoad 후 추가 작업이 필요하다면 super 호출은 필수적이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"LocalizedError의 용도","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"오류 및 발생한 이유를 설명하는 메세지를 제공하는 프로토콜이다. 따라서 에러에 따라 다양한 메세지를 보여주고 싶을 때 사용하면 적절한 프로토콜 같다. 프로퍼티들이 정의되어있는데 직관적인 이름으로 설명하지 않아도 무엇인지 알 수 있다."}]}]}],"type":"unorderedList"}]},{"content":[{"anchor":"%EC%97%98%EB%A6%BC%EC%97%90%EA%B2%8C-%EB%8F%84%EC%A0%84%EA%B3%BC%EC%A0%9C%EB%A5%BC-%EB%B0%9B%EC%95%84%EC%84%9C-%ED%95%B4%EA%B2%B0%ED%95%B4%EB%B3%B4%EC%95%98%EB%8B%A4","type":"heading","level":3,"text":"엘림에게 도전과제를 받아서 해결해보았다."}]},{"content":[{"anchor":"%EC%8A%A4%ED%81%AC%EB%A1%A4%EB%B0%94-%EC%97%86%EC%95%A0%EA%B8%B0","type":"heading","level":3,"text":"스크롤바 없애기"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/fPcCuik.png"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"코드로 설정하기"}]}]}]},{"syntax":"swift","code":["scrollView.showsHorizontalScrollIndicator = false ","scrollView.showsVerticalScrollIndicator = false"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"인터페이스 빌더에서 없애기"}]}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/rOkoBw3.png"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"위 사진에서 체크를 풀어주면 된다."}],"type":"paragraph"}]},{"content":[{"text":"직사각형 버튼을 둥근 버튼으로 만들기","anchor":"%EC%A7%81%EC%82%AC%EA%B0%81%ED%98%95-%EB%B2%84%ED%8A%BC%EC%9D%84-%EB%91%A5%EA%B7%BC-%EB%B2%84%ED%8A%BC%EC%9C%BC%EB%A1%9C-%EB%A7%8C%EB%93%A4%EA%B8%B0","level":3,"type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"다양한 방법이 있는데.. 코드로 해주거나 인터페이스 빌더에서 해줄 수 있다. 인터페이스 빌더에서 해주려면"}]},{"syntax":"swift","code":["extension UIView {"," @IBInspectable var cornerRadius: CGFloat {"," get {"," return layer.cornerRadius"," }"," set {"," layer.cornerRadius = newValue"," layer.masksToBounds = newValue > 0"," }"," }","}"],"type":"codeListing"},{"type":"paragraph","inlineContent":[{"type":"text","text":"위와 같이 extension을 해서 IBInspectable를 추가해주는 방법이 있다. 하지만 하드코딩이라는 생각이 들어서 아래처럼 코드로 바꿔주는 방식으로 바꾸었다."}]},{"code":["override func viewWillLayoutSubviews() {"," super.viewWillLayoutSubviews()"," calculatorButtons.forEach { button in"," button.layer.cornerRadius = button.layer.frame.size.width \/ 2"," }","}"],"type":"codeListing","syntax":"swift"},{"inlineContent":[{"type":"text","text":"버튼의 너비 \/ 2를 해서 cornerRadius를 설정해준 코드다."},{"text":" ","type":"text"},{"type":"text","text":"이때 "},{"code":"viewWillLayoutSubviews","type":"codeVoice"},{"type":"text","text":" 메소드를 써준다."}],"type":"paragraph"}]},{"content":[{"type":"heading","level":3,"anchor":"viewWillLayoutSubviews","text":"`viewWillLayoutSubviews()`?"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"뷰의 바운드가 최종적으로 결정되는 최초 시점","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"제약이나 오토레이아웃을 사용하지 않았다면, 서브뷰의 레이아웃을 업데이트하기 적합한 시점이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"여러번 중복으로 호출될 수 있다."}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"메인뷰의 서브뷰가 로드되는 경우"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이번 계산기 프로젝트에서는 버튼의 cornerRadius를 설정해주기 위해 해당 메소드를 활용해보았다. (제이티 짱…😇)","type":"text"}]}]}]}]},{"content":[{"text":"커스텀 뷰는 도대체 어떻게 만드는 거야?","level":3,"type":"heading","anchor":"%EC%BB%A4%EC%8A%A4%ED%85%80-%EB%B7%B0%EB%8A%94-%EB%8F%84%EB%8C%80%EC%B2%B4-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%A7%8C%EB%93%9C%EB%8A%94-%EA%B1%B0%EC%95%BC"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/qs5GqaX.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"만드는 과정에서 addArrangedSubview와 addSubView의 용도가 헷갈렸다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"addArrangedSubview는 정렬된 스택뷰의 뷰들 리스트라고 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 ","type":"text"},{"code":"arrangedSubviews 배열","type":"codeVoice"},{"type":"text","text":" 끝에 뷰를 추가하는 메서드인 것이다."}]}]},{"content":[{"inlineContent":[{"text":"그러나 addSubview는 UIView에 정의되어있는 메소드로 다른 View 상단에 서브뷰가 추가된다고 정의되어있다. 그래서 StackView내부에서 addSubview를 해도 반응이 없었던 것이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"정리하자면 StackView에서 subview를 추가하고 싶다면 addArrangedSubview 메소드를 사용해야한다."}],"type":"paragraph"}]}]},{"code":["\/\/ 우여곡절 끝에 만든 커스텀 뷰!!!!!","class FormulaStackView: UIStackView {"," private(set) var element = [String]()"," \/\/ 초기화시 스택뷰 설정을 하는게 전부다."," override init(frame: CGRect) {"," super.init(frame: frame)"," loadView()"," }"," "," required init(coder: NSCoder) {"," super.init(coder: coder)"," loadView()"," }"," \/\/ 이름 대충지었네...참ㅋ 걍 스택뷰 설정메소드...."," private func loadView() {"," self.axis = .horizontal"," self.alignment = .fill"," self.distribution = .fill"," self.spacing = 8"," }"," \/\/ label을 생성하여 스택뷰 하위뷰에 추가한다."," func addLabel(_ text: String) {"," let label = UILabel()"," label.font = .preferredFont(forTextStyle: .title3)"," label.textColor = .white"," label.adjustsFontForContentSizeCategory = true"," label.text = text"," self.addArrangedSubview(label)"," element.append(text.replacingOccurrences(of: \",\", with: \"\"))"," }","}"],"type":"codeListing","syntax":"swift"}]},{"content":[{"text":"private extension을 하면 private 해지지않나?","type":"heading","level":3,"anchor":"private-extension%EC%9D%84-%ED%95%98%EB%A9%B4-private-%ED%95%B4%EC%A7%80%EC%A7%80%EC%95%8A%EB%82%98"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/cAOeXUt.png","type":"image"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"나무와 이야기를 나누다가 발견한 것인데, 기존에 내가 알고있던 것은 private extension을 하면 그 내부 요소들의 접근제어가 private으로 붙는 것으로 알고있었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그러나 "},{"isActive":true,"identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/AccessControl.html#ID25","type":"reference"},{"type":"text","text":"를 확인해보니 private extension을 하고 동일한 파일 내부에서는 fileprivate과 같은 접근제어로 인식해서 private extension을 사용해도 "},{"inlineContent":[{"type":"text","text":"동일한 파일 내에서는 접근"}],"type":"strong"},{"type":"text","text":"할 수 있다는 사실을 알게되었다."}]}]}]}]}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"text":"느낀점","type":"heading","anchor":"%EB%8A%90%EB%82%80%EC%A0%90","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"엘림에게 깜짝 과제를 받고도 뚱뚱해져있는 ViewController를 해결할 수 없었다. 좀더 역할 분담을 할 수 있도록 설계하는 과정에 힘을 써야겠다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"공부도 중요하지만 복습도 틈틈히 하자.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"아맞다리드미!"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/velog.io\/@wonhee010\/stack.addArrangedSubview","isActive":true}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"isActive":true,"identifier":"https:\/\/bannavi.tistory.com\/261","type":"reference"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621437-viewwilllayoutsubviews","isActive":true,"type":"reference"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uiview\/1622616-addsubview","type":"reference","isActive":true}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uistackview\/1616227-addarrangedsubview","isActive":true,"type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/parkbi.tistory.com\/20","isActive":true}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/nexthops.tistory.com\/62","isActive":true,"type":"reference"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/stackoverflow.com\/questions\/9837003\/how-to-hide-remove-the-scrollbar-in-uiscrollview-in-iphone\/9837117"}]}]}],"type":"unorderedList"}]}]}],"kind":"content"}],"variants":[{"paths":["\/documentation\/arinote\/211119_viewdidload,_localizederror,_scrollview_bar_hide,_addsubview,_ibinspectable,_cornerradius,_viewwilllayoutsubviews,_private_extension"],"traits":[{"interfaceLanguage":"swift"}]}],"schemaVersion":{"patch":0,"minor":3,"major":0},"kind":"article","sections":[],"metadata":{"modules":[{"name":"Ari's Notes"}],"title":"211119 viewDidLoad, LocalizedError, ScrollView bar hide, addSubView, IBInspectable, cornerRadius, viewWillLayoutSubviews, private extension","role":"article","roleHeading":"Article"},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211119_viewDidLoad,_LocalizedError,_ScrollView_bar_hide,_addSubView,_IBInspectable,_cornerRadius,_viewWillLayoutSubviews,_private_extension","interfaceLanguage":"swift"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"references":{"https://nexthops.tistory.com/62":{"type":"link","url":"https:\/\/nexthops.tistory.com\/62","identifier":"https:\/\/nexthops.tistory.com\/62","title":"블로그 둥근버튼 만들기","titleInlineContent":[{"type":"text","text":"블로그 둥근버튼 만들기"}]},"https://i.imgur.com/qs5GqaX.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/qs5GqaX.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/qs5GqaX.png"}]},"https://stackoverflow.com/questions/9837003/how-to-hide-remove-the-scrollbar-in-uiscrollview-in-iphone/9837117":{"type":"link","url":"https:\/\/stackoverflow.com\/questions\/9837003\/how-to-hide-remove-the-scrollbar-in-uiscrollview-in-iphone\/9837117","identifier":"https:\/\/stackoverflow.com\/questions\/9837003\/how-to-hide-remove-the-scrollbar-in-uiscrollview-in-iphone\/9837117","title":"How to hide\/remove the scrollbar in UIScrollview in iPhone?","titleInlineContent":[{"type":"text","text":"How to hide\/remove the scrollbar in UIScrollview in iPhone?"}]},"https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html#ID25":{"type":"link","url":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/AccessControl.html#ID25","identifier":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/AccessControl.html#ID25","title":"공식문서","titleInlineContent":[{"type":"text","text":"공식문서"}]},"https://i.imgur.com/fPcCuik.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/fPcCuik.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/fPcCuik.png"}]},"https://i.imgur.com/cAOeXUt.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/cAOeXUt.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/cAOeXUt.png"}]},"https://parkbi.tistory.com/20":{"type":"link","url":"https:\/\/parkbi.tistory.com\/20","identifier":"https:\/\/parkbi.tistory.com\/20","title":"블로그 인터페이스 빌더 추가하기","titleInlineContent":[{"type":"text","text":"블로그 인터페이스 빌더 추가하기"}]},"https://developer.apple.com/documentation/uikit/uistackview/1616227-addarrangedsubview":{"type":"link","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uistackview\/1616227-addarrangedsubview","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uistackview\/1616227-addarrangedsubview","title":"공식문서 addArrangedSubview","titleInlineContent":[{"type":"text","text":"공식문서 addArrangedSubview"}]},"https://bannavi.tistory.com/261":{"type":"link","url":"https:\/\/bannavi.tistory.com\/261","identifier":"https:\/\/bannavi.tistory.com\/261","title":"StackView에 대해서","titleInlineContent":[{"type":"text","text":"StackView에 대해서"}]},"https://velog.io/@wonhee010/stack.addArrangedSubview":{"type":"link","url":"https:\/\/velog.io\/@wonhee010\/stack.addArrangedSubview","identifier":"https:\/\/velog.io\/@wonhee010\/stack.addArrangedSubview","title":"블로그 addArrangedSubview","titleInlineContent":[{"type":"text","text":"블로그 addArrangedSubview"}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://developer.apple.com/documentation/uikit/uiviewcontroller/1621437-viewwilllayoutsubviews":{"type":"link","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621437-viewwilllayoutsubviews","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621437-viewwilllayoutsubviews","title":"공식문서 viewWillLayoutSubviews()","titleInlineContent":[{"type":"text","text":"공식문서 viewWillLayoutSubviews()"}]},"https://developer.apple.com/documentation/uikit/uiview/1622616-addsubview":{"type":"link","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uiview\/1622616-addsubview","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uiview\/1622616-addsubview","title":"공식문서 addSubview","titleInlineContent":[{"type":"text","text":"공식문서 addSubview"}]},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/rOkoBw3.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/rOkoBw3.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/rOkoBw3.png"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211121_oslog.json b/docs/data/documentation/arinote/211121_oslog.json new file mode 100644 index 0000000..0bb6e9e --- /dev/null +++ b/docs/data/documentation/arinote/211121_oslog.json @@ -0,0 +1 @@ +{"schemaVersion":{"major":0,"patch":0,"minor":3},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"primaryContentSections":[{"kind":"content","content":[{"type":"heading","level":1,"text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned"},{"inlineContent":[{"text":"11월 21일 (일)","type":"text"}],"type":"paragraph"},{"level":2,"type":"heading","text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"개발자에게 보여주기 위한 print문..? OSLog란?"},{"type":"text","text":" "},{"text":" ","type":"text"}]}]}]},{"type":"heading","level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"계산기 프로젝트 진행시 개발자에게만 보여지는 print문을 작성해보았는데, 알아보니 개발자들을 위해 log를 찍는 방법이 따로 있었다."}],"type":"paragraph"}]}]},{"text":"OSLog란?","anchor":"OSLog%EB%9E%80","type":"heading","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"과거의 데이터를 읽기 위한 통합 로깅 시스템","type":"text"},{"type":"text","text":" "},{"type":"text","text":"OSLog 프레임워크는 사용자(프로그래머)가 로그를 읽을 수 있게 해준다"},{"type":"text","text":" "},{"type":"text","text":"통합 로깅 시스템을 사용하면 instrument 및 Console과 같은 Apple 툴과 함께 사용할 사용자 지정 디버깅 및 분석 툴을 구축할 수 있게 해준다."}]}]}],"type":"unorderedList"},{"text":"통합 로깅 시스템(unified logging system)","level":2,"type":"heading","anchor":"%ED%86%B5%ED%95%A9-%EB%A1%9C%EA%B9%85-%EC%8B%9C%EC%8A%A4%ED%85%9Cunified-logging-system"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"통합이란?"},{"type":"text","text":" "},{"type":"text","text":"로그 데이터 저장을 level에 따라 메모리와 디스크의 데이터 저장소에 모으는 것(iOS 10+ 에서 가능)"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"장점","type":"text"},{"type":"text","text":" "},{"type":"text","text":"모든 레벨의 시스템에서 로깅 표출이 가능하다"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Xcode를 실행하지 않아도 console앱을 실행시키면 로깅이 가능하다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"os_log를 사용하면 mac의 콘솔 앱에서 로그를 쉽게 확인할 수 있다. (필터도 가능)","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"저장 기능 등을 활용할 수 있다."}]}]}],"type":"unorderedList"},{"type":"heading","text":"Logging","level":2,"anchor":"Logging"},{"type":"paragraph","inlineContent":[{"type":"text","text":"통합 로깅 시스템을 사용하여 디버깅 및 성능 분석을 위해 앱에서 원격 측정(telemetry)을 캡쳐한다."}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"통합 로깅 시스템은 시스템의 모든 수준에서 원격 측정을 캡쳐할 수 있는 포괄적이고 성능이 뛰어난 API를 제공한다. 이 시스템은 데이터를 텍스트 기반 로그 파일에 쓰지 않고 메모리 및 디스크에 로그 데이터를 중앙 집중식으로 저장한다. 콘솔 앱, 로그 명령줄 도구 또는 Xcode 디버그 콘솔을 사용하여 로그 메세지를 보거나 OSLog 프레임워크를 사용하여 로그메세지에 프로그래밍 방식으로 엑세스할 수도 있다."}]},{"text":"level","type":"heading","level":2,"anchor":"level"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"주로 Fault를 제외한 4가지를 사용한다.","type":"text"}]}]}]},{"type":"table","header":"row","rows":[[[{"inlineContent":[{"type":"text","text":"level 종류"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"text":"disk에 저장","type":"text"}]}],[{"inlineContent":[{"text":"내용","type":"text"}],"type":"paragraph"}]],[[{"inlineContent":[{"text":"Default (notice)","type":"text"}],"type":"paragraph"}],[{"inlineContent":[{"type":"text","text":"o"}],"type":"paragraph"}],[{"inlineContent":[{"type":"text","text":"문제 해결을 위한 level"}],"type":"paragraph"}]],[[{"type":"paragraph","inlineContent":[{"text":"Info","type":"text"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"o"}]}],[{"type":"paragraph","inlineContent":[{"text":"Error케이스와 유사하지만, 에러 설명이 긴 경우, 문제해걀시 활용할 수 있는, 도움이 되지만 필수적이지 않은 정보","type":"text"}]}]],[[{"type":"paragraph","inlineContent":[{"type":"text","text":"Debug"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"x"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"개발 환경에서의 간단한 로깅 (mac의 ‘콘솔’앱에는 찍히지 않고 xcode console에만 표출)"}]}]],[[{"type":"paragraph","inlineContent":[{"text":"Error","type":"text"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"o"}]}],[{"inlineContent":[{"type":"text","text":"Info케이스와 유사하지만, 간단한 에러인 경우, 활동 객체가 존재하는 경우 관련 프로세스 체인에 대한 정보 캡쳐"}],"type":"paragraph"}]],[[{"inlineContent":[{"type":"text","text":"Fault"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"o"}]}],[{"inlineContent":[{"type":"text","text":"Error와 유사하지만 시스템 레벨 or 다중 프로세스 오류 캡쳐를 위한 것"}],"type":"paragraph"}]]]},{"level":2,"type":"heading","anchor":"%EC%A0%9C%EC%95%BD%EC%82%AC%ED%95%AD","text":"제약사항"},{"inlineContent":[{"text":"통합 로깅 시스템은 iOS 10, macOS 10.12, tvOS 10.0, watchOS 3.0 이후 버전부터 지원한다.","type":"text"}],"type":"paragraph"},{"level":2,"type":"heading","text":"os_log로 로그 찍어보기","anchor":"oslog%EB%A1%9C-%EB%A1%9C%EA%B7%B8-%EC%B0%8D%EC%96%B4%EB%B3%B4%EA%B8%B0"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"os_log(_:)"}]}]}]},{"syntax":"swift","type":"codeListing","code":["os_log(\"error\")"]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/bAMUbbn.png"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"os_log(","type":"text"},{"type":"emphasis","inlineContent":[{"text":":log","type":"text"}]},{"text":":)","type":"text"}],"type":"paragraph"}]}]},{"type":"codeListing","syntax":"swift","code":["os_log(.fault, log: .default, \"error\")"]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/py8HLbd.png","type":"image"}],"type":"paragraph"},{"type":"heading","text":"extension을 활용하기","level":2,"anchor":"extension%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0"},{"inlineContent":[{"type":"text","text":"아래와 같이 OSLog 클래스의 인스턴스를 OSLog 프레임워크에 타입 프로퍼티로 만들어 확장해주면 원하는 subsystem과 카테고리로 메세지를 로깅할 수 있다."}],"type":"paragraph"},{"type":"codeListing","code":["extension OSLog {"," private static var subsystem = Bundle.main.bundleIdentifier!"," static let error = OSLog(subsystem: subsystem, category: \"Error\")","}","","\/\/ 메세지 로깅이 필요한 위치에서 아래 메소드를 호출","os_log(.error, log: .error, “%@“, OSLogMessage.error)"],"syntax":"swift"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/jaB9tGn.png"}],"type":"paragraph"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Zz2Rjpb.png"}],"type":"paragraph"},{"text":"StaticString 타입 장벽 해결하기","type":"heading","level":2,"anchor":"StaticString-%ED%83%80%EC%9E%85-%EC%9E%A5%EB%B2%BD-%ED%95%B4%EA%B2%B0%ED%95%98%EA%B8%B0"},{"inlineContent":[{"text":"메세지 타입으로 StaticString을 요구하는데, 아래와 같은 방법으로 우회적으로 적용해볼 수 있다.","type":"text"}],"type":"paragraph"},{"code":["let message = \"에러 발생\"","os_log(.error, log: .error, \"%@\", message)"],"syntax":"swift","type":"codeListing"},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@ryan-son\/Swift-%ED%86%B5%ED%95%A9-%EB%A1%9C%EA%B9%85-%EC%8B%9C%EC%8A%A4%ED%85%9C%EC%9C%BC%EB%A1%9C-%EB%A1%9C%EA%B9%85-%ED%95%B4%EB%B3%B4%EC%9E%90-OSLog"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/zeddios.tistory.com\/979"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/os\/logging"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/os\/logging\/generating_log_messages_from_your_code"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/ios-development.tistory.com\/381"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}],"kind":"article","variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211121_oslog"]}],"sections":[],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211121_OSLog","interfaceLanguage":"swift"},"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"211121 OSLog"},"references":{"https://i.imgur.com/jaB9tGn.png":{"identifier":"https:\/\/i.imgur.com\/jaB9tGn.png","variants":[{"url":"https:\/\/i.imgur.com\/jaB9tGn.png","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/py8HLbd.png":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/py8HLbd.png","variants":[{"url":"https:\/\/i.imgur.com\/py8HLbd.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/Zz2Rjpb.png":{"identifier":"https:\/\/i.imgur.com\/Zz2Rjpb.png","variants":[{"url":"https:\/\/i.imgur.com\/Zz2Rjpb.png","traits":["1x","light"]}],"alt":null,"type":"image"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://i.imgur.com/bAMUbbn.png":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/bAMUbbn.png","variants":[{"url":"https:\/\/i.imgur.com\/bAMUbbn.png","traits":["1x","light"]}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211122_arc,_oslog,_debugprint,_assert,_initializer,_scrollrecttovisible,_cgrect.json b/docs/data/documentation/arinote/211122_arc,_oslog,_debugprint,_assert,_initializer,_scrollrecttovisible,_cgrect.json new file mode 100644 index 0000000..0f357b0 --- /dev/null +++ b/docs/data/documentation/arinote/211122_arc,_oslog,_debugprint,_assert,_initializer,_scrollrecttovisible,_cgrect.json @@ -0,0 +1 @@ +{"metadata":{"title":"211122 ARC, OSLog, debugPrint, assert, initializer, scrollRectToVisible, CGRect","roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}]},"schemaVersion":{"patch":0,"major":0,"minor":3},"primaryContentSections":[{"content":[{"level":1,"type":"heading","text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned"},{"inlineContent":[{"text":"11월 22일 (월)","type":"text"}],"type":"paragraph"},{"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"text":"학습 내용"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"ARC 활동학습"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"메모리 영역을 4가지로 나누는 이유"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"weak는 언제 써야할지 알겠는데, unowned는 언제써야해?"}]}]}]}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"계산기 프로젝트 STEP 3 피드백 고민"}]}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"개발자에게 보여주기 위한 print문..?","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"OSLog"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"debugPrint","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"assert"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"override init과 required init의 차이점은?"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"계산기 모둠 프로젝트를 하면서 알게된 사실","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"scrollRectToVisible()을 이용해서 자동스크롤을 구현할 수 있었다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":" "}]}]}]}]}]},{"level":2,"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법"},{"text":"메모리 영역을 4가지로 나누는 이유","type":"heading","level":3,"anchor":"%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD%EC%9D%84-4%EA%B0%80%EC%A7%80%EB%A1%9C-%EB%82%98%EB%88%84%EB%8A%94-%EC%9D%B4%EC%9C%A0"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"우리가 어떠한 프로그램을 구현할 때 각각의 변수, 함수, 클래스 등이 ","type":"text"},{"code":"호출되고 해제되는 시기가 다르기 때문","type":"codeVoice"},{"text":"이다. 만약 어떠한 함수 내에서 한번 사용되는 변수가 프로그램의 처음부터 끝까지 메모리에 남아있다면 메모리가 낭비되는 일이 될 것이다.","type":"text"}]}]}],"type":"unorderedList"},{"level":3,"text":"ARC가 하는 것과 못하는 것?","anchor":"ARC%EA%B0%80-%ED%95%98%EB%8A%94-%EA%B2%83%EA%B3%BC-%EB%AA%BB%ED%95%98%EB%8A%94-%EA%B2%83","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"힙에 메모리를 자동으로 해제해주어 메모리관리를 대신 처리해주지만 "},{"type":"codeVoice","code":"참조사이클까지는 자동으로 처리해주지 못한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이를 해결하려면 약한 참조와 미소유참조 통해 해결할 수 있다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"heading","text":"unowned는 언제 써야해?","anchor":"unowned%EB%8A%94-%EC%96%B8%EC%A0%9C-%EC%8D%A8%EC%95%BC%ED%95%B4","level":3},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"weak는 참조가 해제될 때 nil을 넣어주고 unowned는 nil을 할당하지 못하고 해제된 메모리 주소값을 계속 들고 있는다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"성능이 중요하거나 "},{"type":"codeVoice","code":"Dangling pointer가 있는 위험을 감수"},{"text":"하겠다고 하면 unowned를 사용한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"weak와 달리 unowned는 다른 인스턴스보다 ","type":"text"},{"code":"긴 생명주기를 가진 인스턴스를 참조","type":"codeVoice"},{"text":"할 때 주로 사용한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"절대 먼저 메모리에서 해제될 일 없다는 걸 보장할 수 있고 시간\/공간\/계산의 복잡성을 제거할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"객체의 라이프 사이클이 명확하고 개발자에 의해 제어 가능이 명확한 경우 weak Optional 타입 대신 사용하여 좀 더 간결한 코딩이 가능하다.","type":"text"}]}]}]},{"name":"Note","type":"aside","style":"note","content":[{"inlineContent":[{"text":"Dangling pointer란?","type":"text"},{"type":"text","text":" "},{"type":"text","text":"원래 바라보던 인스턴스가 해제되면서 할당되지 않는 공간을 바라보는 포인터"}],"type":"paragraph"}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"OSLog는 ","type":"text"},{"type":"reference","isActive":true,"identifier":"https:\/\/github.com\/leeari95\/TIL\/blob\/main\/2021-11\/211121%20OSLog.md"},{"type":"text","text":"를 해보았는데 나머지 두 부분은 한번 알아봐야겠다."}],"type":"paragraph"}]}]},{"level":3,"type":"heading","text":"debugPrint()","anchor":"debugPrint"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"print()의 경우 인스턴스를 출력할 때 핵심 내용만을 출력해주지만 debugPrint()의 경우 해당 인스턴스의 상세 내역까지 함께 출력해준다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"디버깅 진행 시 쓸데없는 내용까지 친절하게 다 출력해주므로 약간의 속도 저하나 찍혀있는 로그 내용을 읽는데 불편을 주는 경우도 있다.","type":"text"}]}]}]},{"anchor":"assert","level":3,"type":"heading","text":"assert"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"특정 조건을 체크","type":"codeVoice"},{"text":"하고 조건이 성립되지 않으면 ","type":"text"},{"code":"메세지를 출력","type":"codeVoice"},{"type":"text","text":"하게 할 수 있다. 실제 배포된 앱 성능에는 영향을 끼치지 않는다는 특징이 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"주로 디버깅 중 조건의 검증을 위하여 사용한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"API 테스트 등, 조건 체크 및 테스트를 해야할 때 유용하게 사용할 수 있다."}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"text":"override init 과 required init의 차이","level":3,"type":"heading","anchor":"override-init-%EA%B3%BC-required-init%EC%9D%98-%EC%B0%A8%EC%9D%B4"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"override init","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"상속한 부모의 이니셜라이저를 재정의한 것","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"required init","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"상속받았을 때 필수로 정의해야하는 이니셜라이저","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"heading","anchor":"scrollRectToVisible","text":"scrollRectToVisible","level":3},{"syntax":"swift","type":"codeListing","code":["DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1 ) {"," self.calculationHistoryScrollView"," .scrollRectToVisible(CGRect(x: 0,"," y: self.calculationHistoryScrollView.contentSize.height"," - self.calculationHistoryScrollView.bounds.height,"," width: self.calculationHistoryScrollView.bounds.size.width,"," height: self.calculationHistoryScrollView.bounds.size.height),"," animated: true)","}"]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"니코는 나와 아예 다른 방법으로 Scroll View의 자동스크롤을 구현을 했었다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"설명하자면 "},{"code":"DispatchQueue.main.asyncAfter","type":"codeVoice"},{"type":"text","text":" 메소드를 활용하여 "},{"type":"codeVoice","code":"0.1초 딜레이"},{"type":"text","text":"를 준 후 레이아웃을 계산하여 스크롤을 하는 방식이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스택뷰가 추가되고 난 후 0.1초 뒤 해당 메소드가 실행되어 스크롤 뷰의 레이아웃을 계산한다고 보면 된다."}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"scrollRectToVisible()"},{"type":"text","text":" 메소드는 콘텐츠의 특정 영역을 스크롤 하는 메소드다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"setContentOffset()"},{"type":"text","text":" 메소드와 다른 점은 "},{"inlineContent":[{"type":"text","text":"CGPoint"}],"type":"strong"},{"type":"text","text":"가 아니라 "},{"type":"strong","inlineContent":[{"text":"CGRect","type":"text"}]},{"text":"을 이용하여 스크롤을 한다는 차이점이 있는 것 같다.","type":"text"}]}]}]},{"anchor":"CGPoint%EC%99%80-CGRect%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90%EC%9D%80-%EB%AD%98%EA%B9%8C","type":"heading","text":"CGPoint와 CGRect의 차이점은 뭘까?","level":3},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스크롤을 할때 사용하는 두 메소드의 파라미터 타입이 달라서 궁금해졌다."}]}]}]},{"code":["func scrollRectToVisible(_ rect: CGRect, "," animated: Bool)"],"syntax":"swift","type":"codeListing"},{"type":"codeListing","syntax":"swift","code":["func setContentOffset(_ contentOffset: CGPoint, "," animated: Bool)"]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"CGPoint","type":"text"}]}]},{"items":[{"content":[{"inlineContent":[{"text":"2차원 좌표계의 점을 포함하는 타입","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"x좌표와 y좌표를 가지는 타입이라고 보면 될 것 같다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"CGSize"}]}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"너비와 높이 값을 포함하는 타입"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이것도 Point와 마찬가지로 너비와 높이의 값을 가지는 타입으로 보면 될 것 같다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"inlineContent":[{"text":"CGRect","type":"text"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"사각형의 위치와 크기를 포함하는 타입"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Rectangle의 약자인 듯"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"너비와 높이를 가지고 있을 뿐만 아니라 원점도 가지고 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정의 부분을 살펴보면 origin은 CGPoint, size는 CGSize로 정의되어있다."}]}]}],"type":"unorderedList"}]}]},{"type":"aside","style":"note","name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"현실에서는 너비와 높이만 있어도 사각형을 그릴 순 있지만 iOS에서는 ","type":"text"},{"type":"codeVoice","code":"위치"},{"text":"를 알아야 그릴 수 있다.","type":"text"}]}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"https:\/\/leeari95.tistory.com\/15","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/swift\/1539920-debugprint"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/swift\/1541112-assert"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/uiscrollview\/1619439-scrollrecttovisible"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/uiscrollview\/1619400-setcontentoffset"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/zeddios.tistory.com\/201"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/swift\/1541112-assert"}]}]}]}]}]}],"kind":"content"}],"variants":[{"paths":["\/documentation\/arinote\/211122_arc,_oslog,_debugprint,_assert,_initializer,_scrollrecttovisible,_cgrect"],"traits":[{"interfaceLanguage":"swift"}]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211122_ARC,_OSLog,_debugPrint,_assert,_initializer,_scrollRectToVisible,_CGRect","interfaceLanguage":"swift"},"sections":[],"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://github.com/leeari95/TIL/blob/main/2021-11/211121%20OSLog.md":{"titleInlineContent":[{"text":"일요일날 공부","type":"text"}],"identifier":"https:\/\/github.com\/leeari95\/TIL\/blob\/main\/2021-11\/211121%20OSLog.md","title":"일요일날 공부","url":"https:\/\/github.com\/leeari95\/TIL\/blob\/main\/2021-11\/211121%20OSLog.md","type":"link"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211123_app_life_cycle,_init(frame:),_init(coder:),_frame,_bounds,_iboutlet_didset,_updateviewconstraints.json b/docs/data/documentation/arinote/211123_app_life_cycle,_init(frame:),_init(coder:),_frame,_bounds,_iboutlet_didset,_updateviewconstraints.json new file mode 100644 index 0000000..3d0a072 --- /dev/null +++ b/docs/data/documentation/arinote/211123_app_life_cycle,_init(frame:),_init(coder:),_frame,_bounds,_iboutlet_didset,_updateviewconstraints.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211123_App_Life_Cycle,_init(frame:),_init(coder:),_frame,_bounds,_IBOutlet_didSet,_updateViewConstraints","interfaceLanguage":"swift"},"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"211123 App Life Cycle, init(frame:), init(coder:), frame, bounds, IBOutlet didSet, updateViewConstraints"},"kind":"article","sections":[],"schemaVersion":{"minor":3,"patch":0,"major":0},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211123_app_life_cycle,_init(frame:),_init(coder:),_frame,_bounds,_iboutlet_didset,_updateviewconstraints"]}],"primaryContentSections":[{"kind":"content","content":[{"type":"heading","anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","level":1},{"type":"paragraph","inlineContent":[{"text":"11월 23일 (화)","type":"text"}]},{"text":"학습 내용","type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"items":[{"content":[{"inlineContent":[{"text":"Applicedation Life Cycle","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UIView의 이니셜라이저 init(frame:)과 init(coder:)의 차이점?","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Frame vs Bounds"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"IBOutlet의 didSet은 언제 trigger될까?"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"계산기 프로젝트 버튼의 속성값을 언제 넣어줘야 적절할까?","type":"text"},{"text":" ","type":"text"},{"type":"text","text":" "}],"type":"paragraph"}]}],"type":"unorderedList"},{"level":2,"text":"고민한 점 \/ 해결 방법","type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"unorderedList","items":[{"content":[{"text":"App Life Cycle","type":"heading","level":3,"anchor":"App-Life-Cycle"},{"type":"paragraph","inlineContent":[{"type":"text","text":"앱은 foreground일 때와 background일 때 앱이 할 수 있는 일과 해야하는 일이 달라진다."},{"type":"text","text":" "},{"type":"text","text":"foreground인 앱은 시스템 자원의 높은 우선 순위를 가지고"},{"type":"text","text":" "},{"type":"text","text":"background인 앱은 최대한 적은 일(가능하면 아무것도 안하기)을 해야한다."}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자들은 누구나 그렇듯이 앱을 사용할 때"},{"text":" ","type":"text"},{"text":"이 앱 저 앱을 왔다갔다 하거나 잠깐 다른 앱을 보고 오거나 하기 때문에","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"앱의 상태 변화에 따라 반응을 해줄 필요가 있다."}]},{"inlineContent":[{"type":"text","text":"앱의 상태가 변화했는지 어떻게 알 수 있을까?"}],"type":"paragraph"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"iOS 13 이상에서는 UISceneDelegate object를 통해 Scene-based 앱의 life cycle에 대응하고"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/vDWiiL7.png","type":"image"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"그 이전 버전에서는 UIApplicationDelegate objcet를 통해 대응한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/xl7ShNE.png"}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/8icyHFK.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"iOS 12 이하 Scene을 지원하지 않는다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"그래서 UIKit은 모든 life-cycle events를 UIAplicationDelegate object에 전달한다."},{"text":" ","type":"text"},{"type":"text","text":"App delegate는 앱의 모든 window를 관리하고 app state의 전환은 앱의 전체 UI에 영향을 미친다."}]},{"type":"heading","level":2,"text":"iOS 13 이후 바뀐점","anchor":"iOS-13-%EC%9D%B4%ED%9B%84-%EB%B0%94%EB%80%90%EC%A0%90"},{"inlineContent":[{"type":"text","text":"iOS 12까지는 하나의 앱에 하나의 window, 즉 한 앱을 동시에 키는 것이 불가능 하였지만"},{"type":"text","text":" "},{"text":"iOS 13부터는 window의 개념이 Scene으로 대체되고 아래의 그림처럼 하나의 앱에서 여러개의 ","type":"text"},{"type":"reference","isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/app_and_environment\/scenes"},{"type":"text","text":"을 가질 수 있게 되었다. 즉 하나의 앱을 동시에 켜는 것이 가능해졌다."}],"type":"paragraph"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/HElGceR.jpg","type":"image"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"AppDelegate의 역할중 UI의 상태를 알 수 있는 UILifeCycle에 대한 부분을 SceneDelegate가 대체한다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"AppDelegate의 역할중에 UISceneSession Lifecycle이 추가되었다."}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/F7jVek4.png"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SceneSession이 생성되거나 삭제될 때 AppDelegate에게 알려주는 두 가지의 메소드가 AppDelegate에 추가된 것을 확인할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SceneSession은 앱에서 생성한 모든 Scene의 정보를 관리한다."}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/j0jyDQP.png"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"위 그림은 애플 문서에서 제공하는 Scene의 상태 전환을 보여주는 그림이다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자나 시스템이 앱의 Scene을 요청하면 UIKit이 이를 생성하고 Unattached상태로 둔다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"요청자가 사용자라면 Scene을 바로 Foreground로 전환한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"요청자가 시스템이라면 Scene을 Background로 전환하여 이벤트를 처리할 수 있도록 한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"사용자가 앱의 UI를 닫으면 UIKit은 Scene을 Background로 전환하고 곧 Suspended 상태로 전환한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIKit은 Background, Suspended 상태의 앱은 언제든지 연결을 끊고 Unattached 상태로 전환할 수 있다."}]}]}]},{"level":3,"anchor":"%EC%95%B1%EC%9D%B4-%EC%8B%A4%ED%96%89%ED%95%98%EB%8A%94-%EA%B4%80%EC%A0%90","type":"heading","text":"앱이 실행하는 관점"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"먼저 UIKit이 Scene을 앱에 연결한다. 그런 뒤 Scene의 초기 UI를 구성하고 필요한 데이터를 로드한다"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Foreground Active 상태로 전환할 때 UI를 구성하고 사용자와 상호작용할 준비를 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Foreground Active > Foreground Inactive로 전환할 때는 데이터를 저장한다","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Foreground에서 Background로 전환할 땐 중요한 작업을 완료하고 메모리를 확보한 뒤 현재 앱 UI를 스냅샷으로 준비한다. 이 화면이 앱 전환기에서 보이는 그 화면이다."}]}]},{"content":[{"inlineContent":[{"text":"Scene과의 연결이 끊어지면 Scene과 관련된 모든 리소스를 정리한다. (Suspeded 상태)","type":"text"}],"type":"paragraph"}]}],"type":"orderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":"간단하게 앱에서 이러한 작업들이 어떻게 진행되는지 살펴보자."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"sceneWillEnterForeground, sceneDidBecomeActive","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/jFG547g.gif","type":"image"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"sceneWillResignActive, sceneDidBecomeActive"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/O8P8j4p.gif"}]},{"items":[{"content":[{"inlineContent":[{"text":"sceneDidEnterBackground","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/G6AFPco.gif"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"sceneDidDisconnect"}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/OAjHu3V.gif"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"이것이 바로 iOS 13 이후에서 Scene을 사용할 때의 App Life Cycle이고, iOS 12에서 AppDelegate가 이를 관리할 땐 조금 달라진다."}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"iOS 12까지는 UIApplicationDelegate에서 모든 Life Cycle 이벤트를 처리했다."},{"text":" ","type":"text"},{"type":"text","text":"이 때는 아래와 같이 상태 전환이 발생했다고 한다."}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/pfQSi2q.png"}]},{"inlineContent":[{"type":"text","text":"Scene과 비슷하지만 상태의 이름이 다른 것을 확인할 수 있다."},{"type":"text","text":" "},{"text":"AppDelegate는 별도의 Scene에 표시되는 Window를 포함하여 앱의 모든 window를 관리한다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"결과적으로 앱 상태 전환은 외부 디스플레이의 콘텐츠를 포함하여 앱 전체 UI에 영향을 준다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"앱이 시작하면 UI가 화면에 표시될 것인지에 대한 정보에 따라 앱을 Inactive, background 상태로 전환한다."},{"text":" ","type":"text"},{"type":"text","text":"만약 화면에 표시가 될 것이라면 알아서 Active 상태로 전환하고 앱이 종료될 때 까지 Active와 Background 상태를 유지한다."}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"이러한 방식을 App transition이라고 하며 이 방법을 사용할 때의 과정은 아래와 같다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"시작시 앱의 자료구조와 UI를 초기화 한다"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Active 상태가 되면 UI 구성을 완료하고 사용자와 상호작용할 준비를 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"비활성화되면 데이터를 저장하고 앱 동작을 최소화한다."}]}]},{"content":[{"inlineContent":[{"text":"Deactivation 상태가 되면 중요한 작업을 완료하고 메모리를 최대한 확보한 뒤 앱 스냅샷을 준비한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"앱이 종료되면 작업을 즉시 중지하고 공유 자원을 해제한다.","type":"text"}],"type":"paragraph"}]}]},{"type":"heading","anchor":"Respond-to-Other-Significant-Events-%EA%B8%B0%ED%83%80-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%9D%91%EB%8B%B5","text":"Respond to Other Significant Events (기타 이벤트 응답)","level":2},{"type":"paragraph","inlineContent":[{"text":"Life Cycle 이벤트 처리 외에도 앱은 아래와 같이 나열된 이벤트를 처리할 준비가 되어있어야 한다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"UIApplicationDelegate 객체를 사용하여 이러한 이벤트 대부분을 처리한다."},{"type":"text","text":" "},{"text":"경우에 따라 알림을 사용하여 처리할 수도 있으므로 앱의 다른 부분에서 응답할 수 있다.","type":"text"}]}]}]},{"rows":[[[{"inlineContent":[{"text":"-","type":"text"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"text":"-","type":"text"}]}]],[[{"inlineContent":[{"text":"Memory warnings","type":"text"}],"type":"paragraph"}],[{"inlineContent":[{"type":"text","text":"앱이 메모리 사용량이 너무 높을 때 수신된다. 앱에서 사용하는 메모리 양을 줄인다."}],"type":"paragraph"}]],[[{"inlineContent":[{"type":"text","text":"Protected data becomes available\/unavailable"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"text":"사용자가 기기를 잠그거나 잠금 해제할 때 수신된다.","type":"text"}]}]],[[{"type":"paragraph","inlineContent":[{"type":"text","text":"Handoff tasks"}]}],[{"type":"paragraph","inlineContent":[{"text":"객체를 처리해야할 때 수신된다.","type":"text"}]}]],[[{"inlineContent":[{"type":"text","text":"Time changes"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"전화 통신사가 시간 업데이트를 보내는 경우와 같이 여러가지 다른 시간 변경에 대해 수신된다."}]}]],[[{"inlineContent":[{"type":"text","text":"Open URLs"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"text":"앱에서 리소스를 열어야 할 때 수신된다.","type":"text"}]}]]],"type":"table","alignments":["center","center"],"header":"row"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"init(frame:)"},{"type":"text","text":"와 "},{"type":"codeVoice","code":"init(coder:)"},{"text":"의 차이점","type":"text"}],"type":"paragraph"},{"type":"heading","text":"init(frame:)","level":3,"anchor":"initframe"},{"inlineContent":[{"type":"text","text":"코드로 UIView Class의 View 인스턴스를 만들기 위해 지정된 이니셜라이저."},{"type":"text","text":" "},{"text":"Frame rectangle을 가지고 구현하고자 하는 뷰의 중심과 경계선을 지정해준다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":"스토리보드, xib, nib 같은 인터페이스 빌더를 사용하지 않고 코드로 UIView Calss의 View object를 만들기 위해 지정된 이니셜라이저다."}],"type":"paragraph"},{"anchor":"initcoder","level":3,"text":"init(coder:)","type":"heading"},{"inlineContent":[{"text":"기본적으로 ","type":"text"},{"code":"storyboard","type":"codeVoice"},{"type":"text","text":"나 "},{"type":"codeVoice","code":"xib"},{"type":"text","text":"를 활용하면 별도의 코딩 없이 앱의 속성을 수정할 수가 있는데 이것을 가능하게 해주는 과정을 "},{"code":"unarchiving","type":"codeVoice"},{"text":"이라고 한다. Interface builder는 코드가 아니기 때문에 앱을 컴파일 하는 시점에서 컴파일러가 인식할 수 없고 이를 코드로 변환해주는 unarchiving 과정이 필요하다는 것이다.","type":"text"},{"text":" ","type":"text"},{"text":"이 과정에서 ","type":"text"},{"code":"init?(coder:)","type":"codeVoice"},{"text":"가 사용된다.","type":"text"},{"text":" ","type":"text"},{"text":"파라미터 coder를 통해 NSCoder 타입의 객체가 전달되는 것이고 전달된 NSCoder 타입의 객체가 decoding되어 초기화된 후 컴파일 할 수 있게 decoding된 자기자신(self)을 반환하는 작업이라고 보면 될 것 같다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"내가 구성한 View의 상태를 앱의 disk에 저장하는 과정을 "},{"code":"serialize","type":"codeVoice"},{"text":"라고 한다. deserialize는 반대로 disk에 저장된 상태를 불러오는 작업이라고 볼 수 있다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"NSCoding이라는 프로토콜을 통해 이 serialize와 deserialize 작업이 가능해진다."}]},{"inlineContent":[{"text":"따라서 init(coder:)의 용도는 아래와 같다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Storyboard라는 인터페이스 빌더를 사용하여 뷰의 상태를 수정할 경우 serialization 작업을 Xcode가 init(coder:)를 호출하여 앱 내 뷰 작업을 저장하고 불러오는 작업을 해준다."}]}]}]},{"inlineContent":[{"type":"text","text":"UIView 선언부를 보면 NSCoding 프로토콜을 채택하고 있는데 NSCoding 선언부를 살펴보면 실패가능한 이니셜라이저를 작성하도록 되어있다."},{"type":"text","text":" "},{"text":"프로토콜을 준수하는 클래스에서 프로토콜에서 요구하는 이니셜라이저 요구사항을 구현하려면 required 키워드가 붙어야 한다. 따라서 이를 상속받은 FormulaStackView와 같은 클래스에서는 스토리보드르 사용하고 있지 않지만 init?(coder:)를 구현해줘야 하고 앞에 꼭 required를 붙여주어야 한다.","type":"text"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"Frame","type":"codeVoice"},{"type":"text","text":" vs "},{"type":"codeVoice","code":"Bounds"}]}]},{"content":[{"type":"heading","level":2,"text":"Frame","anchor":"Frame"},{"inlineContent":[{"type":"text","text":"Super View 좌표계에서 View의 위치와 크기를 나타낸다."}],"type":"paragraph"}]},{"content":[{"anchor":"Frame%EC%9D%98-originx-y","type":"heading","text":"Frame의 origin(x, y)","level":3},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Super view의 원점을 (0,0)으로 놓고 원점으로부터 얼마나 떨어져 있는지를 나타낸다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 Frame의 origin 값을 변경하면 SubView도 그만큼 같이 움직인다."}]}]}]}]},{"content":[{"anchor":"Frame%EC%9D%98-Sizewidth-height","level":3,"text":"Frame의 Size(width, height)","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"View 영역을 모두 감싸는 사각형으로 나타낸다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"View 자체의 크기가 아니라 View가 차지하는 영역을 감싸서 만든 사각형이라고 이해하면 된다.","type":"text"}]}]}]}]},{"content":[{"text":"언제 사용할까?","type":"heading","level":3,"anchor":"%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"UIView의 위치 및 크기를 설정할 때 사용한다.","type":"text"}]}]}]},{"type":"thematicBreak"}]},{"content":[{"type":"heading","anchor":"Bounds","level":2,"text":"Bounds"},{"type":"paragraph","inlineContent":[{"text":"자신의 좌표계에서 View의 위치와 크기를 나타낸다.","type":"text"}]}]},{"content":[{"type":"heading","level":3,"anchor":"bounds%EC%9D%98-originx-y","text":"bounds의 origin(x, y)"},{"items":[{"content":[{"inlineContent":[{"text":"Super view와는 아무 상관 없으면 기준이 자기 자신이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 좌표의 시작점을 자기의 원점(0,0)으로 놓는다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"bounds를 바꿔줘야 하는 경우는 ScrollView의 ContentOffset을 설정할 때이다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"ScrollView의 SubView 중 어떤 영역을 보여줄지 정할 때 사용할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"bounds의 origin 값을 변경하면 viewport가 변경되며, 뷰가 움직이는 것이 아니라 보여지는 뷰의 좌표가 달라지는 것이라고 이해하면 될 것 같다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉 View의 viewport가 변경된다는 것은 내 View의 SubView의 어디 부분을 보여줄지를 바꿔준다는 뜻"}]}]},{"content":[{"inlineContent":[{"text":"예시로 스크롤 뷰에서 이미지를 확대하여 움직여서 이동하는 것을 viewport 값이 바뀌는 것으로 이해하면 될 것 같다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"level":3,"type":"heading","text":"Bounds의 size(width, height)","anchor":"Bounds%EC%9D%98-sizewidth-height"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"View 자체의 영역을 나타낸다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Frame과 다르게 View영역을 모두 감싸서 만든 사각형이 아니라 View 자체의 영역을 나타낸다고 이해해보면 된다."}]}]}],"type":"unorderedList"}]},{"content":[{"text":"언제 사용할까?","anchor":"%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C","type":"heading","level":3},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"View를 회전(transfomation)한 후 View의 실제 크기를 알고 싶을 때 사용한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"View 내부에 그림을 그릴 때(drawRect) 사용한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ScrollView에서 스크롤링 할 때 사용한다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"ScrollView의 SubView 중 어떤 영역을 보여줄지 정할 때 사용할 수 있다.","type":"text"}]}]}]}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"text":"IBOutlet의 didSet은 언제 trigger될까?","level":3,"type":"heading","anchor":"IBOutlet%EC%9D%98-didSet%EC%9D%80-%EC%96%B8%EC%A0%9C-trigger%EB%90%A0%EA%B9%8C"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"utlet 프로퍼티는 class가 막 초기화 될 때는 nil 상태"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"nib으로부터 object가 초기화 될 때에 값을 갖게 된다"}]}]},{"content":[{"inlineContent":[{"text":"모든 Outlet이 nil이 아님을 확신할 수 있을 때는 viewDidLoad때","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 Outlet에 있는 didSet 옵저버는 viewDidLoad 바로 전에 호출","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 Outlet 프로퍼티 다룰 때 조심해야 함. 예를들어 prepareForeSegue 때 접근하려고 하면 nil이 나올 것.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"IBOutlet은 class라 자체적으로 값을 대입하여 바꾸지 않는 이상 didSet 호출이 되지 않는다."}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"heading","text":"버튼의 conrnerRadius의 값을 줄 때 어디서 줘야 적절할까?","anchor":"%EB%B2%84%ED%8A%BC%EC%9D%98-conrnerRadius%EC%9D%98-%EA%B0%92%EC%9D%84-%EC%A4%84-%EB%95%8C-%EC%96%B4%EB%94%94%EC%84%9C-%EC%A4%98%EC%95%BC-%EC%A0%81%EC%A0%88%ED%95%A0%EA%B9%8C","level":3},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Aqm8zFf.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"엘림의 피드백에 따라 해당 메서드가 언제 호출되는지 확인해보았다. 버튼을 누를 때마다 호출 되었다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 버튼의 속성을 주는 for문이 버튼을 누를 때마다 계속 실행되고 있었다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/ZhTzujz.png"}]}]},{"content":[{"inlineContent":[{"text":"버튼의 IBOutlet에 didSet을 줄 수도 있었지만 아래와 같은 문제 때문에 해당 방법은 불가능 했다.","type":"text"}],"type":"paragraph"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/JPJ9H8A.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"디버깅을 해보니 viewWillLayoutSubviews 메소드가 호출되는 시점에 레이아웃이 갱신되어 버튼의 너비가 바뀌고 있었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"IBOutlet의 didSet은 viewDidLoad가 호출되기 전에 호출되므로 적절하지 못한 방법이였다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 최소한으로 적게 호출되는 updateViewConstraints메소드를 활용하여 해당 문제를 해결하였다.","type":"text"}]},{"type":"codeListing","code":["func setupButtons() {"," calculatorButtons.forEach { button in"," button.layoutIfNeeded()"," button.layer.cornerRadius = button.layer.bounds.width \/ 2"," }","}","","override func updateViewConstraints() {"," super.updateViewConstraints()"," setupButtons()","}"],"syntax":"swift"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"updateViewConstraints에 버튼셋팅 메소드를 호출하고있고, for문 내부에서 버튼마다 layoutIfNeeded를 호출하여 레이아웃을 갱신하고 이후 cornerRadius 값을 대입해주고있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"레이아웃을 갱신하는 이유는 갱신하지 않으면 위 디버깅시 발견하였던 최종 레이아웃이 아니라 임의로 잡혀있던 frame값으로 계산을 하기 때문에 아래처럼 찌그러진 원이 나온다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/O9e089d.png"}]}]}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"level":2,"anchor":"%EB%8A%90%EB%82%80%EC%A0%90","text":"느낀점","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"아니.. App Life Cycle 배울것이 왜이렇게 많지…"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"View Life Cycle에 대해서도 배워야할 것 같다…"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"죽여줘….","type":"text"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/app_and_environment\/managing_your_app_s_life_cycle","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621379-updateviewconstraints"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/sujinnaljin.medium.com\/swift-class%EC%99%80-struct%EC%97%90%EC%84%9C-didset%EC%9D%98-%EC%B0%A8%EC%9D%B4-f784e34ea33f","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/stackoverflow.com\/questions\/38197785\/when-how-outlet-didset","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/babbab2.tistory.com\/44","type":"text"}]}]}],"type":"unorderedList"}]}]}]}],"references":{"https://i.imgur.com/ZhTzujz.png":{"identifier":"https:\/\/i.imgur.com\/ZhTzujz.png","variants":[{"url":"https:\/\/i.imgur.com\/ZhTzujz.png","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/j0jyDQP.png":{"identifier":"https:\/\/i.imgur.com\/j0jyDQP.png","variants":[{"url":"https:\/\/i.imgur.com\/j0jyDQP.png","traits":["1x","light"]}],"alt":null,"type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/pfQSi2q.png":{"identifier":"https:\/\/i.imgur.com\/pfQSi2q.png","variants":[{"url":"https:\/\/i.imgur.com\/pfQSi2q.png","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/xl7ShNE.png":{"identifier":"https:\/\/i.imgur.com\/xl7ShNE.png","variants":[{"url":"https:\/\/i.imgur.com\/xl7ShNE.png","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/O8P8j4p.gif":{"identifier":"https:\/\/i.imgur.com\/O8P8j4p.gif","variants":[{"url":"https:\/\/i.imgur.com\/O8P8j4p.gif","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/jFG547g.gif":{"identifier":"https:\/\/i.imgur.com\/jFG547g.gif","variants":[{"url":"https:\/\/i.imgur.com\/jFG547g.gif","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/F7jVek4.png":{"identifier":"https:\/\/i.imgur.com\/F7jVek4.png","variants":[{"url":"https:\/\/i.imgur.com\/F7jVek4.png","traits":["1x","light"]}],"alt":null,"type":"image"},"https://developer.apple.com/documentation/uikit/app_and_environment/scenes":{"title":"Scene","titleInlineContent":[{"text":"Scene","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/app_and_environment\/scenes","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/app_and_environment\/scenes","type":"link"},"https://i.imgur.com/Aqm8zFf.png":{"identifier":"https:\/\/i.imgur.com\/Aqm8zFf.png","variants":[{"url":"https:\/\/i.imgur.com\/Aqm8zFf.png","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/OAjHu3V.gif":{"identifier":"https:\/\/i.imgur.com\/OAjHu3V.gif","variants":[{"url":"https:\/\/i.imgur.com\/OAjHu3V.gif","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/8icyHFK.png":{"identifier":"https:\/\/i.imgur.com\/8icyHFK.png","variants":[{"url":"https:\/\/i.imgur.com\/8icyHFK.png","traits":["1x","light"]}],"alt":null,"type":"image"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/JPJ9H8A.png":{"identifier":"https:\/\/i.imgur.com\/JPJ9H8A.png","variants":[{"url":"https:\/\/i.imgur.com\/JPJ9H8A.png","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/G6AFPco.gif":{"identifier":"https:\/\/i.imgur.com\/G6AFPco.gif","variants":[{"url":"https:\/\/i.imgur.com\/G6AFPco.gif","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/O9e089d.png":{"identifier":"https:\/\/i.imgur.com\/O9e089d.png","variants":[{"url":"https:\/\/i.imgur.com\/O9e089d.png","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/HElGceR.jpg":{"variants":[{"url":"https:\/\/i.imgur.com\/HElGceR.jpg","traits":["1x","light"]}],"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/HElGceR.jpg"},"https://i.imgur.com/vDWiiL7.png":{"variants":[{"url":"https:\/\/i.imgur.com\/vDWiiL7.png","traits":["1x","light"]}],"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/vDWiiL7.png"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211125_auto_layout,_view_life_cycle,_staticstring,_fork--_repository_-----.json b/docs/data/documentation/arinote/211125_auto_layout,_view_life_cycle,_staticstring,_fork--_repository_-----.json new file mode 100644 index 0000000..b1f0502 --- /dev/null +++ b/docs/data/documentation/arinote/211125_auto_layout,_view_life_cycle,_staticstring,_fork--_repository_-----.json @@ -0,0 +1 @@ +{"kind":"article","schemaVersion":{"patch":0,"major":0,"minor":3},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211125_Auto_Layout,_View_Life_Cycle,_StaticString,_fork--_repository_-----","interfaceLanguage":"swift"},"metadata":{"roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"role":"article","title":"211125 Auto Layout, View Life Cycle, StaticString, fork해온 repository 잔디적용법"},"primaryContentSections":[{"content":[{"type":"heading","level":1,"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"text":"11월 25일 (목)","type":"text"}]},{"text":"학습 내용","type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"계산기 프로젝트의 의문점"}]},{"items":[{"content":[{"inlineContent":[{"text":"아니 왜 SE 디바이스만 버튼 frame 값이 변경되는거지?","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"OSLog에서 사용하는 StaticString은 뭐하는 애지?"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"왜 fork해온 레파지토리는 잔디가 안심어지니…?","type":"text"},{"text":" ","type":"text"},{"text":" ","type":"text"}],"type":"paragraph"}]}]},{"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","level":2},{"items":[{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/MiyseWl.png"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"3기 캠퍼 갓"},{"type":"codeVoice","code":"수박"},{"text":"덕분에 해당 의문점을 해결할 수 있었다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"HIG에서 장치들의 치수를 확인해서 세로를 보면 200~300정도 차이가 나는 것을 확인할 수 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"사진에서도 볼 수 있듯이 세로 크기가 엄청 차이난다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 작아진 디바이스에 오토 레이아웃을 충족하기 위해 버튼의 크기를 줄일 수 밖에 없다는 추측이 가능해졌다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"이러한 사실을 증명해낼 수도 있다. 직접 레이아웃을 다 계산하여 치수와 맞는지 확인할 수도 있지만… 나중에 시도해봐야겠다. 😇","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"heading","anchor":"%EB%8B%B5%EB%B3%80%EB%B0%9B%EC%9D%80-%EC%99%80%EC%A4%91%EC%97%90-%EB%B0%9B%EC%95%98%EB%8D%98-%EA%BF%80%ED%8C%81","level":3,"text":"답변받은 와중에 받았던 꿀팁"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"didSet으로 UI 요소들의 레이아웃을 잡아주는건 부자연스럽다. 오히려 지금처럼 view가 배치된 후 레이아웃을 잡아준 것이 자연스럽고 좋은 방법이다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그리고 UI 인스턴스를 생성할때 클로저로 선언하는 방법이 있고, 인스턴스로 선언 이후 값을 변경해줄 수도 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"선언 이후 값을 변경해주는 방법은 문맥상 부자연스러울 수 있으므로 지양하는게 좋은 것 같다는 의견.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"let으로 선언후 값을 변경해주는 거니까 부자연스럽다는 늬앙스!"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"애초에 클로저로 선언하여 요소의 속성들을 설정해주고 그대로 쭉 사용하는게 더 자연스럽다!","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"level":3,"type":"heading","anchor":"%EB%B2%84%ED%8A%BC%EC%9D%84-%EC%9B%90%ED%98%95%EC%9C%BC%EB%A1%9C-%EB%A7%8C%EB%93%A4%EC%96%B4%EB%B3%B4%EB%A9%B4%EC%84%9C-%EC%96%BB%EC%96%B4%EB%82%B8-%EA%B2%B0%EB%A1%A0-%EC%A0%95%EB%A6%AC","text":"버튼을 원형으로 만들어보면서 얻어낸 결론 정리"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하위뷰 레이아웃은 viewDidLoad 이후 viewWillLayoutSubviews에서 하위뷰가 배치되면서 잡히게 된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"오토 레이아웃은 다양한 디바이스의 적절한 화면을 보여주기 위한 기능이다."}]}]},{"content":[{"inlineContent":[{"text":"디바이스 화면의 크기에 따라 frame은 일정하지 않을 수도 있다.","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"heading","level":3,"text":"StaticString이란?","anchor":"StaticString%EC%9D%B4%EB%9E%80"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"String은 원래 바이너리에 있는 원래 문자열의 메모리 주소를 알고 그 주위에 전체 데이터 구조를 구축하지만 StaticString은 해당 주소를 저장한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 문자열을 "},{"code":"절대 수정하고 싶지않고 고정하고 싶을 때","type":"codeVoice"},{"type":"text","text":" 활용하는 타입이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"예제를 통해 이해해보자","type":"text"}],"type":"paragraph"}]}]},{"syntax":"swift","code":["var myNumber = 10","let myString = \"My number is \\(myNumber)\"","","print(myString)","myNumber = 20","print(myString)"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"기존에 String을 사용하면 문자열 보간법을 통해 런타임 때 문자열에 정보를 추가할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 위 코드의 결과는 아래와 같다."}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/gbJAMKo.png","type":"image"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"myNumber를 변경하더라도 텍스트는 변경되지 않는 것을 볼 수 있다. 문자열 보간법은 값을 할당하는 순간의 값만 고려하므로 나중에 값을 변경해도 동일한 결과가 나오는 것이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그러나 문자열 보간법을 사용하면 문자열의 값을 컴파일 시점에는 값이 제대로 있는지 확인할 수가 없다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 컴파일 시점에 문자열의 값을 알고싶다면 StaticString 타입을 사용한다."}]}]}]},{"type":"codeListing","syntax":"swift","code":["var myNumber = 10","let myString : StaticString = \"My number is \\(myNumber)\"","","print(myString)"]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"위 코드는 컴파일 에러가 난다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/xz0Pn9n.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그리고 다음과 같은 동작도 불가능하다.","type":"text"}]}]}]},{"type":"codeListing","syntax":"swift","code":["var myNumber = 10","let myString : StaticString = \"My number is \" + \"\\(myNumber)\"","","print(myString)"]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이런식으로 컴파일 시점에 항상 완성된 문자열을 갖도록 개발을 강제할 수 있다."}]}]}],"type":"unorderedList"},{"code":["let myString : StaticString = \"My number is \"","let myString2 : StaticString = \"23\"","","myString2 = myString + myString2"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"위와 같이 문자열을 추가하는 것도 할 수 없다.","type":"text"}]}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"anchor":"fork%ED%95%B4%EC%98%A8-repository-%EC%9E%94%EB%94%94-%EC%A0%81%EC%9A%A9%EB%B0%A9%EB%B2%95","type":"heading","level":2,"text":"fork해온 repository 잔디 적용방법"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"잔디가 심어지는 조건"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"GitHub 계정과 commit 이메일 계정이 동일하거나","type":"text"},{"text":" ","type":"text"},{"text":"commit이 Fork한 repository가 아닌 나만의 repository에서 이루어져야 한다.","type":"text"}]}],"type":"aside","style":"note","name":"Note"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"즉, commit이 fork한 repository에서 commit이 이루어졌기 때문에 잔디가 심어지지 않았던 것이다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"anchor":"%ED%95%B4%EA%B2%B0%EB%B2%95-%F0%9F%91%89%F0%9F%8F%BB-fork%ED%95%B4%EC%98%A8-repository%EB%A5%BC-%EC%83%88-%EB%A0%88%ED%8C%8C%EC%A7%80%ED%86%A0%EB%A6%AC%EB%A1%9C-%EB%B3%B5%EC%82%AC%ED%95%B4%EC%98%A4%EC%9E%90","text":"해결법 👉🏻 fork해온 repository를 새 레파지토리로 복사해오자.","type":"heading","level":3},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"일단 내 Github에 새 repository를 생성한다"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"복사하고 싶은 forked repository 주소를 copy한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"terminal을 열고 copy한 forked repository를 bare clone한다."}],"type":"paragraph"},{"style":"note","content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"git clone --bare "}]}],"name":"Note","type":"aside"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"cd로 방금 clone한 레파지토리 폴더로 진입","type":"text"}]},{"name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"cd "}]}],"style":"note","type":"aside"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"맨처음 생성했던 새로운 레파지토리로 Mirror-push","type":"text"}]},{"style":"note","content":[{"type":"paragraph","inlineContent":[{"code":"git push --mirror <생성했던 레파지토리 주소>","type":"codeVoice"}]}],"type":"aside","name":"Note"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"default 브랜치를 내 브랜치로 설정해주면 끝"}]}]}],"type":"orderedList"}]},{"content":[{"level":3,"type":"heading","text":"주의","anchor":"%EC%A3%BC%EC%9D%98"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"복사해서 만든 레파지토리는 잔디는 심어지지만 "},{"type":"codeVoice","code":"PR은 보낼 수 없다."},{"text":" 프로젝트를 마무리한 후에 하면 좋을 것 같다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"나는 그래서 잔디용 레파지토리라 생각하고 fork 레파지토리를 새 레파지토리에 복제해온 다음 새 레파지토리를 private으로 바꿔버렸다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/m6b8qtk.png","type":"image"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그리고 private 레파지토리도 커밋 반영되도록 설정해주었다. 깔끔!","type":"text"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/ItSTgAB.png","type":"image"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"별건 아니고… 잔디 쬐금 심었다.. 뿌듯…","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/HJfkqvK.png","type":"image"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"text":"느낀점","level":2,"anchor":"%EB%8A%90%EB%82%80%EC%A0%90","type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"역시 의문점들은 그냥 지나치지 않고 계속 파보면 깊이 학습하는데 있어서 많은 도움이 되는 것 같다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"그래도 의문점이 풀릴 수준 까지만 파야겠다. 그 이상은….😇","type":"text"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/visual-design\/adaptivity-and-layout\/"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/swift\/staticstring","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/stackoverflow.com\/questions\/32247387\/difference-between-string-and-staticstring"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/holyswift.app\/why-and-when-to-use-the-swifts-staticstring-struct?x-host=holyswift.app","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/velog.io\/@whoyoung90\/fork-%ED%95%B4%EC%98%A8-repository-%EC%9E%94%EB%94%94-%EC%8B%AC%EB%8A%94-%EB%B0%A9%EB%B2%95","type":"text"}]}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211125_auto_layout,_view_life_cycle,_staticstring,_fork--_repository_-----"]}],"sections":[],"references":{"https://i.imgur.com/gbJAMKo.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/gbJAMKo.png"}],"type":"image","identifier":"https:\/\/i.imgur.com\/gbJAMKo.png","alt":null},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/xz0Pn9n.png":{"variants":[{"url":"https:\/\/i.imgur.com\/xz0Pn9n.png","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/i.imgur.com\/xz0Pn9n.png","alt":null},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/HJfkqvK.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/HJfkqvK.png"}],"type":"image","identifier":"https:\/\/i.imgur.com\/HJfkqvK.png","alt":null},"https://i.imgur.com/m6b8qtk.png":{"variants":[{"url":"https:\/\/i.imgur.com\/m6b8qtk.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/m6b8qtk.png","type":"image","alt":null},"https://i.imgur.com/MiyseWl.png":{"variants":[{"url":"https:\/\/i.imgur.com\/MiyseWl.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/MiyseWl.png","type":"image","alt":null},"https://i.imgur.com/ItSTgAB.png":{"variants":[{"url":"https:\/\/i.imgur.com\/ItSTgAB.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/ItSTgAB.png","type":"image","alt":null}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211126_app_life_cycle,_cfgetretaincount,_readme.json b/docs/data/documentation/arinote/211126_app_life_cycle,_cfgetretaincount,_readme.json new file mode 100644 index 0000000..f1c726d --- /dev/null +++ b/docs/data/documentation/arinote/211126_app_life_cycle,_cfgetretaincount,_readme.json @@ -0,0 +1 @@ +{"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","title":"211126 App Life Cycle, CFGetRetainCount, README","role":"article"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211126_app_life_cycle,_cfgetretaincount,_readme"]}],"kind":"article","primaryContentSections":[{"kind":"content","content":[{"level":1,"anchor":"TIL-Today-I-Learned","type":"heading","text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"type":"text","text":"11월 26일 (금)"}]},{"text":"학습 내용","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"App Life Cycle 활동학습"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"CFGetRetainCount"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"2기, 3기 선배분들과 이야기"},{"type":"text","text":" "},{"text":" ","type":"text"}]}]}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","type":"heading","level":2},{"items":[{"content":[{"level":3,"text":"iOS 13에서 Scene Delegate로 이관된 App Delegate의 역할은 무엇무엇이 있을까?","type":"heading","anchor":"iOS-13%EC%97%90%EC%84%9C-Scene-Delegate%EB%A1%9C-%EC%9D%B4%EA%B4%80%EB%90%9C-App-Delegate%EC%9D%98-%EC%97%AD%ED%95%A0%EC%9D%80-%EB%AC%B4%EC%97%87%EB%AC%B4%EC%97%87%EC%9D%B4-%EC%9E%88%EC%9D%84%EA%B9%8C"},{"inlineContent":[{"type":"text","text":"active, inactive, background, foreground 상태 관여한다."},{"type":"text","text":" "},{"type":"text","text":"즉 UI Lifecycle에 대한 역할이 이관된 것으로 볼 수 있다."},{"text":" ","type":"text"},{"text":"UI Lifecycle은 Application UI의 상태(state)를 알게 해준다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"anchor":"App-Delegate%EC%99%80-Scene-Delegate%EC%9D%98-%EA%B0%81%EA%B0%81%EC%9D%98-%EC%97%AD%ED%95%A0%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C","type":"heading","text":"App Delegate와 Scene Delegate의 각각의 역할은 무엇일까?","level":3},{"type":"aside","name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"App Delegate의 역할은 Process Lifecycle과 Session Lifecycle을 관리한다.","type":"text"}]}],"style":"note"},{"items":[{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"text":"Process Lifecycle?","type":"text"}]},{"type":"text","text":" "},{"type":"text","text":"프로세스의 상태 관여"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"Session Lifecycle?","type":"text"}],"type":"strong"},{"text":" ","type":"text"},{"type":"text","text":"Scene Delegate에서 새로운 Scene Session이 생성되거나 이미 존재하는 Scene 버려질때 AppDelegate에 알려준다."}]}]}],"type":"unorderedList"}]}]},{"style":"note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Scene Delegate는 UI Lifecycle을 관리한다."}]}],"name":"Note","type":"aside"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"UI Lifecycle","type":"text"}],"type":"strong"},{"text":" ","type":"text"},{"text":"active, inactive, background, foreground 상태에 관여한다.","type":"text"}]}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"heading","anchor":"Scene%EC%9D%98-%EA%B0%9C%EB%85%90%EC%9D%B4-%EC%83%9D%EA%B8%B4-%EC%9D%B4%EC%9C%A0%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%B4%EA%B3%A0-%EC%96%B8%EC%A0%9C-%EC%96%B4%EB%94%94%EC%84%9C-%ED%99%9C%EC%9A%A9%ED%95%B4%EB%B3%BC-%EC%88%98-%EC%9E%88%EC%9D%84%EA%B9%8C","text":"Scene의 개념이 생긴 이유는 무엇이고, 언제 어디서 활용해볼 수 있을까?","level":3}]}]},{"style":"note","content":[{"type":"paragraph","inlineContent":[{"text":"아래 그림처럼 하나의 앱이 하나의 process와 하나의 user interface 객체와만 matching되었던 iOS 12 이전까지는 괜찮았다.","type":"text"},{"text":" ","type":"text"},{"type":"image","identifier":"https:\/\/i.imgur.com\/eoD7lSy.png"}]}],"name":"Note","type":"aside"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"그러나 iOS 13 부터는 이전처럼 하나의 process를 사용하긴 하지만 다수의 UI 객체나 Scene Session들을 가지게 되면서 위와 같은 방식이 유효하지 않게 되었다."},{"type":"text","text":" "},{"type":"image","identifier":"https:\/\/i.imgur.com\/sQ485Ez.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"iOS 12까지는 하나의 앱에 하나의 window, 즉 한 앱을 여러개 키는 것이 불가능 했다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"iOS 13부터는 window의 개념이 Scene으로 대체되고 아래의 그림처럼 하나의 앱에서 여러개의 Scene을 가질 수 있게 되었다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"즉 하나의 앱을 여러개 켜는 것이 가능해졌다.","type":"text"},{"text":" ","type":"text"},{"type":"image","identifier":"https:\/\/i.imgur.com\/AdK6tJB.jpg"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"items":[{"content":[{"text":"Life Cycle에서 Unattached, Suspended, Not Running의 메모리와 프로세스의 관점에서의 차이는 무엇일까?","type":"heading","level":3,"anchor":"Life-Cycle%EC%97%90%EC%84%9C-Unattached-Suspended-Not-Running%EC%9D%98-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%99%80-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4%EC%9D%98-%EA%B4%80%EC%A0%90%EC%97%90%EC%84%9C%EC%9D%98-%EC%B0%A8%EC%9D%B4%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C"}]}],"type":"unorderedList"},{"type":"aside","name":"Note","content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"Unattached","type":"text"}],"type":"strong"},{"type":"text","text":" "},{"type":"text","text":"시스템이 connection notification을 주기전 까지는 이 상태를 유지한다."},{"type":"text","text":" "},{"text":"따라서 메모리를 점유하고 있고 실행중인 상태이다.","type":"text"},{"type":"text","text":" "},{"type":"strong","inlineContent":[{"type":"text","text":"Suspended"}]},{"text":" ","type":"text"},{"type":"text","text":"Scene이 background 상태에 있으며 아무것도 실행되지 않는 상태를 의미한다."},{"type":"text","text":" "},{"text":"따라서 메모리는 점유하고 있지만 대기중인 상태라고 할 수 있다.","type":"text"},{"type":"text","text":" "},{"type":"strong","inlineContent":[{"type":"text","text":"Not Running"}]},{"type":"text","text":" "},{"type":"text","text":"아예 App이 실행되지 않았거나 실행이 되었지만 시스템에 의해서 종료된 상태이다."},{"type":"text","text":" "},{"type":"text","text":"따라서 메모리에도 없고 프로세스의 관점에서도 아무것도 실행되지 않는다."}]}],"style":"note"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"heading","level":3,"text":"App Life Cycle 모식도의 점선과 실선의 차이는 무엇일까?","anchor":"App-Life-Cycle-%EB%AA%A8%EC%8B%9D%EB%8F%84%EC%9D%98-%EC%A0%90%EC%84%A0%EA%B3%BC-%EC%8B%A4%EC%84%A0%EC%9D%98-%EC%B0%A8%EC%9D%B4%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C"}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/laUoP7a.png"}],"type":"paragraph"},{"style":"note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"점선","type":"text"}],"type":"strong"},{"text":" ","type":"text"},{"text":"특별한 event가 없어도 시스템이 자동으로 수행해주는 상태의 전환이라고 할 수 있다.","type":"text"},{"type":"text","text":" "},{"type":"strong","inlineContent":[{"type":"text","text":"실선"}]},{"type":"text","text":" "},{"type":"text","text":"사용자나 시스템에 의해 발생한 event로 인해서 발생하는 상태의 전환이라고 할 수 있다."}]}],"name":"Note"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"CFGetRetainCount?","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"참조 카운트를 확인할 때 사용하는 타입이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"파라미터를 보니 CFTypeRef 라는 타입을 받고있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"code":["func CFGetRetainCount(_ cf: CFTypeRef!) -> CFIndex"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"inlineContent":[{"text":"공식문서를 확인해보니 AnyObject를 typealias로 선언한 타입이였다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"syntax":"swift","type":"codeListing","code":["typealias CFTypeRef = AnyObject"]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"파라미터로 객체를 받고 간단히 그 객체에 대한 참조카운트를 반환해주는 타입으로 이해하면 될 것 같다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":" "}]}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"면접 질문은 1학기, 2학기에 공부했던 내용 위주로 나왔었다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"딥하게 상세히 공부할 필요는 없다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"대신 물어보면 대답할 정도의 지식은 가지고 있어야한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"질문에 답을 해보는 연습을 하면 좋을 것 같다."}]},{"items":[{"content":[{"inlineContent":[{"text":"음.. 가볍게 하루에 하나씩 면접 질문에 답변을 달아보는 것도 괜찮을…????","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"네카라쿠배를 가려면 CS 기초지식이 탄탄해야한다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"난 백지인데….쩝…"}]}]}]}]},{"content":[{"inlineContent":[{"text":"자신이 목표로 하는 회사를 목표삼아 공부 계획을 세워보면 좋다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로젝트 README는 면접관들이 보기 좋게 정리하는 것이 좋다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"지금은 기록해두는 것만으로도 충분히 잘하고 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"일단 기록하고 나중에 보는 사람이 편하도록 정리해두면 된다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"캠프가 끝난 후 2주동안 면접 준비를 해서 "},{"code":"경험이다","type":"codeVoice"},{"text":" 생각하고 작은 회사들부터 면접을 보러 다닌다. 그리고 면접을 통해서 깨져보고 부족한 부분을 다시 채워가는 것이 좋다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"TIL이나"}],"type":"strong"},{"text":" ","type":"text"},{"type":"strong","inlineContent":[{"text":"README","type":"text"}]},{"text":" 등 ","type":"text"},{"inlineContent":[{"text":"기록하는 것","type":"text"}],"type":"strong"},{"type":"text","text":"이 제일 중요하다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"커리어 캠프에서 배우는 것, 야곰이 주는 키워드 위주로 공부를 하면 된다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"커리어 캠프는 장기간 레이스다. 남은 캠프기간동안 지치지 않도록 체력 보충을 위해 쉬는것도 중요하다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"점점 갈수록 힘들어지니 쉴땐 쉬어야 한다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"엘렌: 저도 방학땐 공부를 했었는데… 지금 다시 방학이 온다고하면 쉴거 같습니다. ㅎㅎ"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"샤피로: 저같은 경우에도 방학에 공부를 하긴 했는데.. 딥하게 한건 아니고 가볍게 부족했던 부분을 알아보는 시간을 가졌어요."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"3기 선배님들의 말씀중 캠프 막판에 아프신 분들이 많았다고 한다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"난 일단 놀고 시간이 남는다면 공부를 하던가 말던가… 방학인데 뭐 맘놓고 놀아야지 ㅎㅎ"}]}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"anchor":"%EA%B9%83%EB%AA%A8%EC%A7%80","text":"깃모지?","level":3,"type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"2기 캠퍼 ","type":"text"},{"identifier":"https:\/\/github.com\/Fezravien\/re-ios-open-market","isActive":true,"type":"reference"},{"type":"text","text":"를 구경하다가 발견했다."}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/3tuKs4C.png","type":"image"}]},{"level":4,"anchor":"%E2%9C%8F%EF%B8%8F-Commit-Message","type":"heading","text":"✏️ Commit Message"},{"inlineContent":[{"text":"기능 단위로 나눠 개발하는 과정의 커밋 메시지는 ","type":"text"},{"type":"codeVoice","code":"깃 이모지"},{"text":"를 활용해서 가시성과 일관성을 높혔다.","type":"text"}],"type":"paragraph"},{"alignments":["left","center","left"],"header":"row","type":"table","rows":[[[{"inlineContent":[{"text":"Type","type":"text"}],"type":"paragraph"}],[{"inlineContent":[{"text":"Emoji","type":"text"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"Description"}]}]],[[{"type":"paragraph","inlineContent":[{"type":"text","text":"Feat"}]}],[{"inlineContent":[{"text":"✨","type":"text"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"기능 (새로운 기능)"}]}]],[[{"inlineContent":[{"type":"text","text":"Fix"}],"type":"paragraph"}],[{"inlineContent":[{"type":"text","text":"🐛"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"text":"버그 (버그 수정)","type":"text"}]}]],[[{"inlineContent":[{"type":"text","text":"Refactor"}],"type":"paragraph"}],[{"inlineContent":[{"type":"text","text":"♻️"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"text":"리팩토링 ","type":"text"},{"type":"codeVoice","code":"기능 변경 없음"}]}]],[[{"type":"paragraph","inlineContent":[{"type":"text","text":"Style"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"🚚"}]}],[{"type":"paragraph","inlineContent":[{"text":"파일 형식\/네이밍, 폴더 구조\/네이밍 수정하거나 옮기는 작업 ","type":"text"},{"type":"codeVoice","code":"비즈니스 로직에 변경 없음"}]}]],[[{"inlineContent":[{"type":"text","text":"Style"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"text":"💄","type":"text"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"스타일 (UI 스타일 변경) "},{"type":"codeVoice","code":"비즈니스 로직에 변경 없음"}]}]],[[{"inlineContent":[{"text":"Docs","type":"text"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"📝"}]}],[{"type":"paragraph","inlineContent":[{"text":"문서 (문서 추가, 수정, 삭제)","type":"text"}]}]],[[{"inlineContent":[{"text":"Test","type":"text"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"✅"}]}],[{"type":"paragraph","inlineContent":[{"text":"테스트 (테스트 코드 추가, 수정, 삭제) ","type":"text"},{"type":"codeVoice","code":"비즈니스 로직에 변경 없음"}]}]],[[{"type":"paragraph","inlineContent":[{"type":"text","text":"Chore"}]}],[{"inlineContent":[{"text":"🔧","type":"text"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"text":"기타 (빌드, 시스템 파일 및 설정 변경)","type":"text"}]}]],[[{"type":"paragraph","inlineContent":[{"type":"text","text":"Comment"}]}],[{"type":"paragraph","inlineContent":[{"text":"💡","type":"text"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"필요한 주석 추가 및 변경"}]}]],[[{"type":"paragraph","inlineContent":[{"text":"Remove","type":"text"}]}],[{"type":"paragraph","inlineContent":[{"text":"🔥","type":"text"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"파일, 폴더 삭제 작업"}]}]]]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"와.. 이거 너무 귀여운데? 나도 써먹어야지~~~ 하고 긁어왔다."}]}]},{"content":[{"inlineContent":[{"text":"그리고 프로젝트를 살펴보니 브랜치를 기능단위로 나누셨다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"리드미만 구경해도 얻는 정보들이 많다는 것을 새삼 느낀다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"나도 기능단위 브랜치 파보기 도전~~~!!!"}]}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"level":2,"anchor":"%EB%8A%90%EB%82%80%EC%A0%90","type":"heading","text":"느낀점"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"쉴땐 쉬고 공부할땐 예습학습복습 열심히 하자~"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"기록도 잊지말자","type":"text"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/hyerios.tistory.com\/148"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/velog.io\/@minni\/iOS-Application-Life-Cycle","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/corefoundation\/1521288-cfgetretaincount","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/corefoundation\/cftyperef"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/ios-development.tistory.com\/692","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/daheenallwhite.github.io\/ios\/interview\/2020\/07\/16\/Interview-Review\/"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/github.com\/Fezravien\/re-ios-open-market"}]}]}]}]}],"type":"unorderedList"}]}],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211126_App_Life_Cycle,_CFGetRetainCount,_README"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"sections":[],"schemaVersion":{"minor":3,"major":0,"patch":0},"references":{"https://i.imgur.com/sQ485Ez.png":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/sQ485Ez.png"}],"identifier":"https:\/\/i.imgur.com\/sQ485Ez.png","alt":null},"https://i.imgur.com/laUoP7a.png":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/laUoP7a.png"}],"identifier":"https:\/\/i.imgur.com\/laUoP7a.png","alt":null},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/eoD7lSy.png":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/eoD7lSy.png"}],"identifier":"https:\/\/i.imgur.com\/eoD7lSy.png","alt":null},"https://i.imgur.com/AdK6tJB.jpg":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/AdK6tJB.jpg"}],"identifier":"https:\/\/i.imgur.com\/AdK6tJB.jpg","alt":null},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://github.com/Fezravien/re-ios-open-market":{"type":"link","title":"Fezz의 README","identifier":"https:\/\/github.com\/Fezravien\/re-ios-open-market","titleInlineContent":[{"type":"text","text":"Fezz의 README"}],"url":"https:\/\/github.com\/Fezravien\/re-ios-open-market"},"https://i.imgur.com/3tuKs4C.png":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/3tuKs4C.png"}],"identifier":"https:\/\/i.imgur.com\/3tuKs4C.png","alt":null}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211203_tableview,_-----.json b/docs/data/documentation/arinote/211203_tableview,_-----.json new file mode 100644 index 0000000..c46aee3 --- /dev/null +++ b/docs/data/documentation/arinote/211203_tableview,_-----.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"metadata":{"roleHeading":"Article","role":"article","title":"211203 TableView, 뷰의재사용","modules":[{"name":"Ari's Notes"}]},"schemaVersion":{"major":0,"patch":0,"minor":3},"sections":[],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211203_tableview,_-----"]}],"kind":"article","identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211203_TableView,_-----"},"primaryContentSections":[{"kind":"content","content":[{"type":"heading","level":1,"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"type":"text","text":"12월 3일 (금)"}]},{"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"TableView란?","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"TableViewCell, DataSource, Delegate"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"뷰의 재사용","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"text":"고민한 점 \/ 해결 방법","level":2,"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[TableView란?]","type":"text"}]}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"테이블뷰는 iOS 애플리케이션에서 많이 활용하는 사용자 인터페이스이다."},{"type":"text","text":" "},{"type":"text","text":"테이블뷰는 리스트 형태를 지니고 있으며 스크롤이 가능해 많은 정보를 보여줄 수 있다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[기본 형태]","type":"text"}]}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"하나의 열(column)과 여러줄의 행(row)을 지니며 수직으로만 스크롤이 가능하다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"각 행은 하나의 셀(cell)에 대응한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"섹션(section)을 이용해 행을 시각적으로 나눌 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"헤더(header)와 푸터(footer)에 이미지나 텍스트를 추가해 추가 정보를 보여줄 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/dVHuBaD.png"}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/eUVGd2x.png","type":"image"}]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[스타일]","type":"text"}],"type":"strong"}]},{"inlineContent":[{"type":"text","text":"테이블뷰는 크게 두 가지 스타일(일반, 그룹)로 나뉜다."}],"type":"paragraph"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/ZmRJopN.png"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"일반 테이블뷰(plain TableView)","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"더이상 나뉘지 않는 연속적인 행의 리스트 형태"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"하나 이상의 섹션을 가질 수 있으며 각 섹션은 여러개의 행을 지닐 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"각 섹션은 헤더 혹은 푸터를 옵션으로 지닐 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"색인을 이용한 빠른 탐색을 하거나 옵션을 선택할 때 용이하다."}]}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/8FwvZh9.png"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"그룹 테이블뷰(Grouped TableView)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"섹션을 기준으로 그룹화되어있는 리스트 형태이다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"하나 이상의 섹션을 가질 수 있으며 각 센션은 여러개의 행을 지닐 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"각 센션은 헤더 혹은 푸터를 옵션으로 지닐 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"정보를 특정 기준에 따라 개념적으로 구분할 때 적합하다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자가 정보를 빠르게 이해하는 데 도움이 된다."}]}]}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[테이블뷰 생성]","type":"text"}]}],"type":"paragraph"},{"inlineContent":[{"text":"테이블뷰를 생성하고 관리하는 좋은 방법은 스토리보드에서 커스텀 UITableViewController 클래스의 객체를 이용하는 것이다. 필요에 따라서 소스코드로 테이블뷰를 생성하는 것도 물론 가능하다.","type":"text"},{"type":"text","text":" "},{"text":"스토리보드에서 테이블뷰의 특성을 지정할 때 동적 프로토타입(dynamic prototypes) 혹은 정적 셀(static cells)중 하나를 선택할 수 있다. 새로운 테이블뷰를 생성할 때 기본 설정값은 동적 프로토타입이다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"동적 프로토타입(Dynamic Prototypes)"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"셀 하나를 디자인해 이를 다른 셀의 템플릿으로 사용하는 방식","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"같은 레이아웃의 셀을 여러개 이용해 정보를 표시할 경우"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"데이터 소스(UITableViewDataSource) 인스턴스에 의해 콘텐츠를 관리하며 셀의 개수가 상황에 따라 변하는 경우에 사용","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"정적 셀(Static Cells)","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"고유의 레이아웃과 고정된 수의 행을 가지는 테이블뷰에 사용","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테이블뷰를 디자인하는 시점에 테이블의 형태와 셀의 개수가 정해져있는 경우 사용"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"셀의 개수가 변하지 않음"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[테이블뷰 구성요소]","type":"text"}]}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"테이블뷰를 구성하기 위해 꼭 알아야 하는 개념에는 셀(Cell), 델리게이트(Delegate), 데이터소스(DataSource)가 있다."}]},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[셀(Cell)]"}],"type":"strong"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"테이블뷰 셀은 테이블뷰를 이루는 개별적인 행(row)으로, UITableViewCell 클래스를 상속받는다. UITableViewCell 클래스에 정의된 표준 스타일을 활용해 문자열 혹은 이미지를 제공하는 셀을 생성할 수 있으며 커스텀 서브뷰를 올려 다양한 시각적 모습을 나타낼 수 있다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[셀의 구조]","type":"text"}]}]},{"inlineContent":[{"type":"text","text":"기본적으로 테이블뷰 셀은 콘텐츠 영역과 액세서리뷰 영역으로 구조가 나뉜다."}],"type":"paragraph"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Qfh8qcg.png"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"콘텐츠 영역","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"셀의 왼족 부분에는 주로 문자열, 이미지 혹은 고유 식별자 등이 입력된다"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"액세서리뷰 영역","type":"text"},{"type":"text","text":" "},{"type":"text","text":"셀의 오른쪽 작은 부분은 액세서리뷰로 상세보기, 재정렬, 스위치 등과 같은 컨트롤 객체가 위치한다."}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/l6Z30qS.png"}]},{"inlineContent":[{"type":"text","text":"테이블뷰를 편집모드(Editing Mode)로 전환하면 아래와 같은 구조로 바뀐다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/8ufnai1.png"}]},{"items":[{"content":[{"inlineContent":[{"text":"편집 컨트롤은 삭제 컨트롤(빨간 원 안의 마이너스 기호)또는 추가 컨트롤(녹색 원 안의 플러스 기호)중 하나가 될 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"재정렬이 가능한 경우 재정렬 컨트롤이 액세서리뷰에 나타난다. 재정렬 컨트롤을 눌러 셀을 드래그하면 위라래로 순서를 변경할 수 있다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/DsA4A87.png","type":"image"}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[테이블뷰 셀의 기본 기능]"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"UITableViewCell 클래스를 상속받는 기본 테이블뷰 셀은 표준스타일을 이용할 수 있다. 표준 스타일의 콘텐츠 영역은 한개 이상의 문자열 그리고 이미지를 지닐 수 있으며, 이미지가 오른쪽으로 확장됨에 따라 문자열이 오른쪽으로 밀려난다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"UITableViewCell 클래스는 셀 콘텐츠에 세가지 프로퍼티가 정의되어 있다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"textLabel: UILabel 주제목 레이블"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"detailTextLabel: UILabel 추가 세부사항 표시를 위한 부제목 레이블","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"imageView: UIImageView 이미지 표시를 위한 이미지뷰","type":"text"}]}]}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/ya5ub06.png"}],"type":"paragraph"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/u1HCXGd.png","type":"image"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[커스텀 테이블뷰 셀]"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"UITableViewCell 클래스에서 제공하는 표준 스타일 셀을 이용해 이미지와 문자열을 표현하고 글꼴 및 색상 등을 수정할 수 있지만, 기본 형태를 벗어나 다양한 애플리케이션의 요구를 충족시키기 위해 셀을 커스텀할 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"셀을 커스텀하면 이미지를 텍스트 오른쪽에 위치시키는 등 원하는 시작적 형태를 만들 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"셀을 커스텀 하는 방법에는 크게 두가지 방법이 있는데, 스토리보드를 이용하거나 코드로 구현할 수 있다."}],"type":"paragraph"}]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/CvgxfmJ.png","type":"image"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[데이블뷰 데이터소스와 델리게이트]"}],"type":"strong"}]},{"inlineContent":[{"type":"text","text":"UITableView 객체는 데이터소스와 델리게이트가 없다면 정상적으로 동작하기 어려우므로 두 객체가 꼭 필요하다."},{"type":"text","text":" "},{"text":"MVC 프로그래밍 디자인 패턴에 따라 데이터소스는 애플리케이션의 데이터모델과 관련되어 있으며, 델리게이트는 테이블뷰의 모양과 동작을 관리하기에 컨트롤러의 역할에 가깝다. 테이블뷰는 뷰의 역할을 한다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":"데이터 소스와 델리게이트 덕분에 테이블뷰를 매우 유연하게 만들 수 있다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/jAYYuru.png"}]},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[데이터 소스]"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"테이블뷰 데이터소스 객체는 UITableViewDataSource 프로토콜을 채택한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터소스는 테이블뷰를 생성하고 수정하는데 필요한 정보를 테이블뷰 객체에 제공한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"데이터소스는 데이터 모델의 델리게이트로 테이블뷰의 시각적 모양에 대한 최소한의 정보를 제공한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"UITableView 객체에 섹션의 수와 행의 수를 알려주며, 행의 삽입, 삭제 및 재정렬하는 기능을 선택적으로 구현할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UITableViewDataSource 프로토콜의 주요 메서드는 아래와 같다. 이중 @required로 선언된 두가지 메서드는 UITableViewDataSource 프로토콜을 채택한 타입에 필수로 구현해야 한다."}]}]}]},{"code":["@required "," \/\/ 특정 위치에 표시할 셀을 요청하는 메서드"," func tableView(UITableView, cellForRowAt: IndexPath) "," "," \/\/ 각 섹션에 표시할 행의 개수를 묻는 메서드"," func tableView(UITableView, numberOfRowsInSection: Int)"," "," @optional"," \/\/ 테이블뷰의 총 섹션 개수를 묻는 메서드"," func numberOfSections(in: UITableView)"," "," \/\/ 특정 섹션의 헤더 혹은 푸터 타이틀을 묻는 메서드"," func tableView(UITableView, titleForHeaderInSection: Int)"," func tableView(UITableView, titleForFooterInSection: Int)"," "," \/\/ 특정 위치의 행을 삭제 또는 추가 요청하는 메서드"," func tableView(UITableView, commit: UITableViewCellEditingStyle, forRowAt: IndexPath)"," "," \/\/ 특정 위치의 행이 편집 가능한지 묻는 메서드"," func tableView(UITableView, canEditRowAt: IndexPath)",""," \/\/ 특정 위치의 행을 재정렬 할 수 있는지 묻는 메서드"," func tableView(UITableView, canMoveRowAt: IndexPath)"," "," \/\/ 특정 위치의 행을 다른 위치로 옮기는 메서드"," func tableView(UITableView, moveRowAt: IndexPath, to: IndexPath)"],"type":"codeListing","syntax":"swift"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[델리게이트]","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테이블뷰 델리게이트 객체는 UITablewViewDelegate 프로토콜을 채택한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"델리게이트는 테이블뷰의 시각적인 부분 수정, 행의 선택 관리, 액세서리뷰 지원, 테이블뷰의 개별 행 편집을 도와준다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"델리게이트 메서드를 활용하면 테이블뷰의 세세한 부분을 조정할 수 있다.","type":"text"}],"type":"paragraph"}]}]},{"syntax":"swift","code":["\/\/ 특정 위치 행의 높이를 묻는 메서드"," func tableView(UITableView, heightForRowAt: IndexPath)"," \/\/ 특정 위치 행의 들여쓰기 수준을 묻는 메서드"," func tableView(UITableView, indentationLevelForRowAt: IndexPath)",""," \/\/ 지정된 행이 선택되었음을 알리는 메서드"," func tableView(UITableView, didSelectRowAt: IndexPath)",""," \/\/ 지정된 행의 선택이 해제되었음을 알리는 메서드"," func tableView(UITableView, didDeselectRowAt: IndexPath)",""," \/\/ 특정 섹션의 헤더뷰 또는 푸터뷰를 요청하는 메서드"," func tableView(UITableView, viewForHeaderInSection: Int)"," func tableView(UITableView, viewForFooterInSection: Int)",""," \/\/ 특정 섹션의 헤더뷰 또는 푸터뷰의 높이를 물어보는 메서드"," func tableView(UITableView, heightForHeaderInSection: Int)"," func tableView(UITableView, heightForFooterInSection: Int)",""," \/\/ 테이블뷰가 편집모드에 들어갔음을 알리는 메서드"," func tableView(UITableView, willBeginEditingRowAt: IndexPath)",""," \/\/ 테이블뷰가 편집모드에서 빠져나왔음을 알리는 메서드"," func tableView(UITableView, didEndEditingRowAt: IndexPath?)"],"type":"codeListing"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[뷰의 재사용?]","type":"text"}]}],"type":"paragraph"},{"inlineContent":[{"text":"iOS 기기는 한정된 메모리를 가지고 애플리케이션을 구동한다. 만약 사용자에게 보여주고 싶은 데이터가 많고 데이터의 양 만큼 많은 뷰가 필요하다면 어떻게 될까?","type":"text"},{"type":"text","text":" "},{"type":"text","text":"화면에 표시할 수 있는 뷰의 개수는 한정되어 있지만 표현해야하는 데이터가 많은 경우 반복된 뷰를 생성하기 보다는 뷰를 재사용할 수 있다."},{"type":"text","text":" "},{"text":"사용할 수 있는 메모리가 작아서 데이터의 양만큼 많은 뷰를 생성하는 것은 메모리를 많이 낭비할 수 밖에 없기 때문이다.","type":"text"},{"text":" ","type":"text"},{"text":"뷰를 재사용하므로써 메모리를 절약하고 성능 또한 향상할 수 있다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[재사용의 대표적인 예]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"UITableViewCell","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"UICollectionViewCell","type":"text"}],"type":"paragraph"}]}]},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[재사용 원리]"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"테이블뷰 및 컬렉션 뷰에서 셀을 표시하기 위해 데이터 소스에 뷰(셀) 인스턴스를 요청한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터 소스는 요청마다 새로운 셀을 만드는 대신 재사용 큐(Reuse Queue)에 재사용을 위해 대기하고있는 셀이 있는지 확인 후 있으면 그 셀에 새로운 데이터를 설정하고 없으면 새로운 셀을 생성한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"테이블뷰 및 컬렉션뷰는 데이터 소스가 셀을 반환하면 화면에 표시한다","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자가 스크롤을 하게 되면 일부 셀들이 화면 밖으로 사라지면서 다시 재사용 큐에 들어간다."}]}]},{"content":[{"inlineContent":[{"text":"위 1번부터 4번까지의 과정이 계속 반복된다.","type":"text"}],"type":"paragraph"}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/6CYaLD3.png"}],"type":"paragraph"},{"text":" ","level":2,"anchor":"","type":"heading"},{"items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/www.boostcourse.org\/mo326\/lecture\/16860?isDesc=false"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/views_and_controls\/table_views","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/views_and_controls\/table_views\/filling_a_table_with_data","type":"text"}]}]}]}]}],"type":"unorderedList"}]}],"references":{"https://i.imgur.com/8FwvZh9.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/8FwvZh9.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/8FwvZh9.png","type":"image"},"https://i.imgur.com/CvgxfmJ.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/CvgxfmJ.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/CvgxfmJ.png"}]},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/ya5ub06.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/ya5ub06.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/ya5ub06.png","type":"image"},"https://i.imgur.com/8ufnai1.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/8ufnai1.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/8ufnai1.png"}]},"https://i.imgur.com/Qfh8qcg.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/Qfh8qcg.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/Qfh8qcg.png","type":"image"},"https://i.imgur.com/dVHuBaD.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/dVHuBaD.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/dVHuBaD.png"}]},"https://i.imgur.com/l6Z30qS.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/l6Z30qS.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/l6Z30qS.png","type":"image"},"https://i.imgur.com/ZmRJopN.png":{"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/ZmRJopN.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/ZmRJopN.png"}]},"https://i.imgur.com/u1HCXGd.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/u1HCXGd.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/u1HCXGd.png","type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/DsA4A87.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/DsA4A87.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/DsA4A87.png","type":"image"},"https://i.imgur.com/6CYaLD3.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/6CYaLD3.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/6CYaLD3.png","type":"image"},"https://i.imgur.com/jAYYuru.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/jAYYuru.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/jAYYuru.png","type":"image"},"https://i.imgur.com/eUVGd2x.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/eUVGd2x.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/eUVGd2x.png","type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211206_tableview_----,_json,_api.json b/docs/data/documentation/arinote/211206_tableview_----,_json,_api.json new file mode 100644 index 0000000..76bae34 --- /dev/null +++ b/docs/data/documentation/arinote/211206_tableview_----,_json,_api.json @@ -0,0 +1 @@ +{"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211206_TableView_----,_JSON,_API","interfaceLanguage":"swift"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211206_tableview_----,_json,_api"]}],"primaryContentSections":[{"kind":"content","content":[{"type":"heading","text":"TIL (Today I Learned)","level":1,"anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"text":"12월 6일 (월)","type":"text"}]},{"text":"학습 내용","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"만국박람회 프로젝트 STEP1 진행","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"JSON 파일을 참고하여 타입 설계"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"TableView 활동학습"}]}]},{"content":[{"inlineContent":[{"text":"TableViewCell, DataSource, Delegate, 뷰의 재사용 다시 ","type":"text"},{"type":"reference","identifier":"https:\/\/github.com\/leeari95\/TIL\/blob\/main\/2021-12\/211203_TableView,_%EB%B7%B0%EC%9D%98%EC%9E%AC%EC%82%AC%EC%9A%A9.md","isActive":true},{"text":"하기","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[TableView Cell Sytle]","type":"text"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/views\/tables\/","type":"reference","isActive":true},{"type":"text","text":"에 들어가니 셀의 스타일들이 나온다."}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/yfhHvNu.png"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[활동학습 다시 스스로 풀어보기]","type":"text"}],"type":"strong"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"스크롤 다운 → (5,2) 터치 → 스크롤 업 → (3,1) 터치 → 스크롤 업 → (1,0)터치 → 스크롤 업 → (0,1) 터치 → (0,0) 터치","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"level":2,"type":"heading","text":"`User` : 앱을 켜야지~","anchor":"User--%EC%95%B1%EC%9D%84-%EC%BC%9C%EC%95%BC%EC%A7%80~"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"Table View","type":"codeVoice"},{"text":"이 ","type":"text"},{"type":"codeVoice","code":"Table View Data Source"},{"text":"에게 : 보여줘야 하는 Section 0의 수는 몇개입니까?","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Table View"},{"text":"이 ","type":"text"},{"type":"codeVoice","code":"Table View Data Source"},{"type":"text","text":"에게 : Section 0은(는) 몇 개의 Row를 보여줘야 합니까?"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Table View Data Source"},{"text":"이 ","type":"text"},{"type":"codeVoice","code":"Cell Reuse Queue"},{"text":"에게 : (0, 0) ~ (0, 2) IndexPath에 보여줄 셀인데, Cell Reuse Identifier에 해당하는 Cell을 주십시오","type":"text"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"heading","text":"`User` : 아래로 스크롤 해야지 스크롤","anchor":"User--%EC%95%84%EB%9E%98%EB%A1%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4-%ED%95%B4%EC%95%BC%EC%A7%80-%EC%8A%A4%ED%81%AC%EB%A1%A4","level":2}]},{"content":[{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Table View"},{"type":"text","text":"이 "},{"code":"Table View Data Source","type":"codeVoice"},{"text":"에게 : 보여줘야 하는 Section 1~8의 수는 몇개입니까?","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Table View Data Source"},{"text":"이 ","type":"text"},{"code":"Cell Reuse Queue","type":"codeVoice"},{"type":"text","text":"에게 : (1, 0) ~ (8, 2) IndexPath에 보여줄 셀인데, Cell Reuse Identifier에 해당하는 Cell을 주십시오"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"Table View Data Source","type":"codeVoice"},{"type":"text","text":"이 "},{"type":"codeVoice","code":"Cell Reuse Queue"},{"type":"text","text":"에게 : Cell Reuse Identifier에 해당하는 Cell 중에 대기중인 Cell이 있어요. 드릴게요."}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Table View Data Source"},{"type":"text","text":"이 "},{"type":"codeVoice","code":"Cell Reuse Queue"},{"text":"에게 : Cell Reuse Identifier에 해당하는 Cell 중 대기중인 Cell이 없어 새로 만들어 드릴게요.","type":"text"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"anchor":"User--%EC%9C%84%EB%A1%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4-%ED%95%B4%EC%95%BC%EC%A7%80-%EC%8A%A4%ED%81%AC%EB%A1%A4","level":2,"type":"heading","text":"`User` : 위로 스크롤 해야지 스크롤"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"Table View","type":"codeVoice"},{"type":"text","text":"이 "},{"type":"codeVoice","code":"Table View Data Source"},{"text":"에게 : 보여줘야 하는 Section 5~8의 수는 몇개입니까?","type":"text"}]}]},{"content":[{"inlineContent":[{"code":"Table View Data Source","type":"codeVoice"},{"text":"이 ","type":"text"},{"type":"codeVoice","code":"Cell Reuse Queue"},{"type":"text","text":"에게 : (5, 1) ~ (8, 2) IndexPath에 보여줄 셀인데, Cell Reuse Identifier에 해당하는 Cell을 주십시오"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Table View Data Source"},{"text":"이 ","type":"text"},{"type":"codeVoice","code":"Cell Reuse Queue"},{"type":"text","text":"에게 : Cell Reuse Identifier에 해당하는 Cell 중에 대기중인 Cell이 있어요. 드릴게요."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/jibM1lI.png"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"heading","anchor":"Table-View-Delegate--5-2-%EC%85%80-%ED%84%B0%EC%B9%98","level":2,"text":"`Table View Delegate` : 5, 2 셀 터치!!"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Table View Delegate"},{"text":"이 ","type":"text"},{"type":"codeVoice","code":"Table View"},{"text":"에게 : 사용자가 S, R의 row를 선택했다구!","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Cell"},{"text":" : 얼럿을 띄워야지","type":"text"}]}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"heading","anchor":"User--%EC%9C%84%EB%A1%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4-%ED%95%B4%EC%95%BC%EC%A7%80-%EC%8A%A4%ED%81%AC%EB%A1%A4","text":"`User` : 위로 스크롤 해야지 스크롤","level":2}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Table View"},{"type":"text","text":"이 "},{"code":"Table View Data Source","type":"codeVoice"},{"type":"text","text":"에게 : 보여줘야 하는 Section 3~6의 수는 몇개입니까?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Table View Data Source"},{"type":"text","text":"이 "},{"code":"Cell Reuse Queue","type":"codeVoice"},{"text":"에게 : (3, 0) ~ (6, 0) IndexPath에 보여줄 셀인데, Cell Reuse Identifier에 해당하는 Cell을 주십시오","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Table View Data Source"},{"type":"text","text":"이 "},{"code":"Cell Reuse Queue","type":"codeVoice"},{"text":"에게 : Cell Reuse Identifier에 해당하는 Cell 중에 대기중인 Cell이 있어요. 드릴게요.","type":"text"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/hpqz3bV.png","type":"image"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"level":2,"type":"heading","text":"`Table View Delegate` : 3, 1 셀 터치!!","anchor":"Table-View-Delegate--3-1-%EC%85%80-%ED%84%B0%EC%B9%98"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Table View Delegate"},{"type":"text","text":"이 "},{"type":"codeVoice","code":"Table View"},{"text":"에게 : 사용자가 S, R의 row를 선택했다구!","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Cell"},{"text":" : 얼럿을 띄워야지","type":"text"}]}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"level":2,"text":"`User` : 위로 스크롤 해야지 스크롤","type":"heading","anchor":"User--%EC%9C%84%EB%A1%9C-%EC%8A%A4%ED%81%AC%EB%A1%A4-%ED%95%B4%EC%95%BC%EC%A7%80-%EC%8A%A4%ED%81%AC%EB%A1%A4"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Table View Data Source"},{"type":"text","text":"이 "},{"type":"codeVoice","code":"Cell Reuse Queue"},{"text":"에게 : (0, 2) ~ (3, 0) IndexPath에 보여줄 셀인데, Cell Reuse Identifier에 해당하는 Cell을 주십시오","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Table View Data Source"},{"type":"text","text":"이 "},{"type":"codeVoice","code":"Cell Reuse Queue"},{"text":"에게 : Cell Reuse Identifier에 해당하는 Cell 중에 대기중인 Cell이 있어요. 드릴게요.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/hzFhHJV.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"text":"`Table View Delegate` : 1, 0 셀 터치!!","level":2,"anchor":"Table-View-Delegate--1-0-%EC%85%80-%ED%84%B0%EC%B9%98","type":"heading"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Table View Delegate"},{"text":"이 ","type":"text"},{"code":"Table View","type":"codeVoice"},{"text":"에게 : 사용자가 S, R의 row를 선택했다구!","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"Cell","type":"codeVoice"},{"type":"text","text":" : 얼럿을 띄워야지"}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/utW2unz.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"text":"`Table View Delegate` : 0, 0 셀 터치!!","anchor":"Table-View-Delegate--0-0-%EC%85%80-%ED%84%B0%EC%B9%98","level":2,"type":"heading"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Table View Delegate"},{"type":"text","text":"이 "},{"type":"codeVoice","code":"Table View"},{"type":"text","text":"에게 : 사용자가 S, R의 row를 선택했다구!"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Cell"},{"type":"text","text":" : 얼럿을 띄워야지"}]}]}]},{"type":"thematicBreak"}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[JSON을 디코딩할 때 프로퍼티들은 왜 변수일까?]"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"구글링을 열심히 해보았지만 정확한 근거는 찾지못했다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따로 공식문서에 기재도 안되어있는거 보니 별로 중요하지도 않고, let 이든 var든 개발자에 의도에 따라 정의해주면 될 것 같다는 결론이 났다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 이번 만국박람회에서는 데이터를 파싱해오고 따로 변경하거나 하지 않을 것이기 때문에 let으로 해주어도 상관 없다는 생각이 들었다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[API란?]","type":"text"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Application Programming Interface)의 약자이다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"API는 점원이다. 레스토랑에서 손님에게 주문 가능한 메뉴를 보여주고 손님이 고른 음식을 주방에 전달 후 음식이 나오면 고객에게 전달하는 ‘점원’이 API라고 볼 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"응용 프로그램에서 사용할 수 있도록 운영체제나, 프로그래밍 언어가 제공하는 기능을 제어할 수 있게 만든 인터페이스를 뜻한다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"inlineContent":[{"type":"text","text":"[Interface란?]"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컴퓨터 시스템끼리 정보를 교환하는 공유 경계를 의미."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"터치스크린과 같은 일부 컴퓨터 하드웨어 장치들은 인터페이스를 통해 데이터를 송수신 할 수 있으며, 마우스나 마이크론 폰가 같은 장치들은 오직 시스템에 데이터를 전송만 하는 인터페이스를 제공한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"간단하게 인터페이스는 사물과 사물 또는 사물과 사람 사이에서 상호간의 소통을 위해 만들어진 매개체나 규약이라고 생각하면 된다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"결국 API는 어떤 프로그램에서 데이터를 주고 받기 위한 방법이 되겠다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Postman을 활용하여 API 직접 찔러보기."}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/RsO2R1J.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/H3jsvpU.png","type":"image"}]}]}],"type":"unorderedList"}]}],"schemaVersion":{"major":0,"patch":0,"minor":3},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"metadata":{"title":"211206 TableView 활동학습, JSON","roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}]},"sections":[],"references":{"https://i.imgur.com/hpqz3bV.png":{"identifier":"https:\/\/i.imgur.com\/hpqz3bV.png","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/hpqz3bV.png"}]},"https://i.imgur.com/utW2unz.png":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/utW2unz.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/utW2unz.png"},"https://i.imgur.com/H3jsvpU.png":{"identifier":"https:\/\/i.imgur.com\/H3jsvpU.png","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/H3jsvpU.png"}]},"https://developer.apple.com/design/human-interface-guidelines/ios/views/tables/":{"url":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/views\/tables\/","titleInlineContent":[{"type":"text","text":"HIG"}],"type":"link","title":"HIG","identifier":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/views\/tables\/"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://github.com/leeari95/TIL/blob/main/2021-12/211203_TableView,_%EB%B7%B0%EC%9D%98%EC%9E%AC%EC%82%AC%EC%9A%A9.md":{"titleInlineContent":[{"type":"text","text":"복습"}],"identifier":"https:\/\/github.com\/leeari95\/TIL\/blob\/main\/2021-12\/211203_TableView,_%EB%B7%B0%EC%9D%98%EC%9E%AC%EC%82%AC%EC%9A%A9.md","type":"link","url":"https:\/\/github.com\/leeari95\/TIL\/blob\/main\/2021-12\/211203_TableView,_%EB%B7%B0%EC%9D%98%EC%9E%AC%EC%82%AC%EC%9A%A9.md","title":"복습"},"https://i.imgur.com/RsO2R1J.png":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/RsO2R1J.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/RsO2R1J.png"},"https://i.imgur.com/hzFhHJV.png":{"identifier":"https:\/\/i.imgur.com\/hzFhHJV.png","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/hzFhHJV.png"}]},"https://i.imgur.com/jibM1lI.png":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/jibM1lI.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/jibM1lI.png"},"https://i.imgur.com/yfhHvNu.png":{"identifier":"https:\/\/i.imgur.com\/yfhHvNu.png","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/yfhHvNu.png"}]},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211207_nsdataasset,_----,_contents.json,_framework,_library,_api.json b/docs/data/documentation/arinote/211207_nsdataasset,_----,_contents.json,_framework,_library,_api.json new file mode 100644 index 0000000..19848b3 --- /dev/null +++ b/docs/data/documentation/arinote/211207_nsdataasset,_----,_contents.json,_framework,_library,_api.json @@ -0,0 +1 @@ +{"schemaVersion":{"major":0,"patch":0,"minor":3},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"sections":[],"kind":"article","primaryContentSections":[{"kind":"content","content":[{"level":1,"text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned","type":"heading"},{"inlineContent":[{"text":"12월 7일 (화)","type":"text"}],"type":"paragraph"},{"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"만국박람회 프로젝트 STEP1 PR 작성"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"NSDataAsset 타입이란?"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"카훗 오답노트"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Contents.JSON?"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"프레임워크와 라이브러리, API 정리","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"heading","text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[NSDataAsset]"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"에셋 카탈로그에 저장된 데이터 집합 타입으로부터의 객체이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 타입을 활용하여 Asset에 있는 JSON 파일을 디코딩해볼 수 있다."}]}]}],"type":"unorderedList"},{"syntax":"swift","code":["\/\/ 파일이름을 전달하면 JSON파일을 디코딩해주는 함수"," func decode(fileName: String) -> T? {"," guard let dataAsset: NSDataAsset = NSDataAsset(name: fileName) else { \/\/ Data 옵셔널바인딩"," return nil"," }"," let decoder: JSONDecoder = JSONDecoder()"," let exposition = try? decoder.decode(T.self, from: dataAsset.data) \/\/ 바인딩한 data를 파라미터로 넘겨줌"," return exposition"," }"],"type":"codeListing"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[카훗 오답노트]"}],"type":"strong"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"ARC를 통한 인스턴스의 생명주기는 컴파일타임에 결정된다","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"백그라운드 사용 앱은 백그라운드 실행 중 시스템이 앱을 언제 종료할지 알 수 있다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"code":"이 앱은 백그라운드에서 실행하게 만들겁니다.","type":"codeVoice"},{"text":" 라고 명시한 것이기 때문에 언제 종료하는지 알수 없으면 안된다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"코드를 통해 프로그래머가 정확하게 확인할 수 있는 사항은?","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"앱이 어떤 방법(홈에서, 푸쉬에서 등)을 통해 실행됐는지"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"앱이 백그라운드로 전환된 시점"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱의 첫 화면이 보여지는 시점"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱이 종료된 시점은 절대 알수가 없다."}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"컴퓨터 CPU의 구성요소가 아닌 것은 RAM이다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"M1칩 같은 경우에는 CPU가 아니라 통합 칩셋이다. RAM과 CPU가 가까이 붙어있기 때문에 빠른 속도를 낼 수 있는 것이다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"LLDB 명령어 중 po를 통해 출력하는 문자열을 임의로 지정하려면 debugDescription 프로퍼티를 구현해야 한다."}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[Contents.JSON]","type":"text"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Asset 카탈로그에 파일을 넣어줄 때 필요한 정보를 가지고 있으며, 이 파일을 Asset에 넣어주게 되면 자동으로 정보를 인식한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Asset 별로 필요한 Key가 따로 정해져 있었다. 이는 Asset Catalog Format Reference에서 확인할 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Image Set type idiom에서 universal을 선택하게 되면 기기, 플랫폼에 관계없이 전부 이미지를 사용할 수 있다."}]}]}]},{"inlineContent":[{"inlineContent":[{"text":"[Framework]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"목적에 따라 효율적으로 구조를 짜놓은 개발 방식"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"애플리케이션의 토대를 뜻한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자가 프레임워크의 규칙을 준수해야한다."}]}]}]},{"name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"특징","type":"text"}]}],"type":"aside","style":"note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"공통적인 개발 환경을 제공한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"개발할 수 있는 범위가 정해져있다."}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Library]"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"특정 기능에 대한 도구\/함수들을 모아둔 집합.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"재사용 가능한 코드의 집합을 뜻한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"사용자가 라이브러리를 가져다 쓴다.","type":"text"}]}]}]},{"name":"Note","style":"note","type":"aside","content":[{"inlineContent":[{"type":"text","text":"특징"}],"type":"paragraph"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"개발하는데 필요한 것들을 모아둔 일종의 저장소를 말한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"목적에 맞는 라이브러리를 필요할 때 호출해서 사용한다.","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[API]"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"다른 프로그램이 제공하는 기능을 제어할 수 있게 만든 인터페이스"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로그램과 운영체제를 연결해 주는 다리 역할을 수행한다."}]}]}]},{"name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"특징","type":"text"}]}],"style":"note","type":"aside"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"다른 프로그램(운영체제)과 연결해주는 다리 역할을 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"구현이 아닌 제어를 담당한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"API를 조합해 획기적인 프로그램을 만들 수 있다"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"예시) 버스 시간을 알려주는 프로그램은 공공 API를 이용한 프로그램이다.","type":"text"}],"type":"paragraph"}]}]}]}]},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[프레임워크와 라이브러리의 차이점]"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"프레임워크는 Flow에 대한 제어 권한을 자체적으로 가지고 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"라이브러리는 Flow에 대한 제어 권한을 사용자가 가지고 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"즉 흐름에 대한 제어 권한이 어디에 있느냐의 차이가 있다.","type":"text"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"집짓기에 비유하자면,"},{"text":" ","type":"text"},{"text":"집의 기본 토대를 애플리케이션 개발에서는 프레임워크 라고 하고,","type":"text"},{"type":"text","text":" "},{"text":"집 안에 전자제품 중 스마트 TV를 다양한 기능들의 집합체인 라이브러리라고 한다.","type":"text"},{"text":" ","type":"text"},{"text":"더불어 다양한 기능들을 제어하는 TV 리모콘을 API라고 한다.","type":"text"}]}],"type":"aside","style":"note","name":"Note"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Foundation은 라이브러리에 대한 설명과도 잘 어울리는 것 같은데?]"}]}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"프레임워크는 라이브러리보다 더 큰 의미이고 추상적인 개념이다. 따라서 프레임워크는 때에 따라 라이브러리가 될 수도 있다는 생각이 들었다."},{"type":"text","text":" "},{"type":"text","text":" "}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/baobab.live\/82"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"호댕의 12\/6일자 TIL"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/stackoverflow.com\/questions\/148747\/what-is-the-difference-between-a-framework-and-a-library"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/www.youtube.com\/watch?v=_j4u4ftWwhQ","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/medium.com\/@hongseongho\/%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC%EC%99%80-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%9D%98-%EC%B0%A8%EC%9D%B4-2f5bf35140ca"}]}]}]}]}]}]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211207_NSDataAsset,_----,_Contents.JSON,_Framework,_Library,_API","interfaceLanguage":"swift"},"metadata":{"modules":[{"name":"Ari's Notes"}],"role":"article","title":"211207 NSDataAsset, 오답노트, Contents.JSON, Framework, Library, API","roleHeading":"Article"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211207_nsdataasset,_----,_contents.json,_framework,_library,_api"]}],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211208_navigation_bar,_uiimageview,_nsmutableattributedstring,_addattribute.json b/docs/data/documentation/arinote/211208_navigation_bar,_uiimageview,_nsmutableattributedstring,_addattribute.json new file mode 100644 index 0000000..1245119 --- /dev/null +++ b/docs/data/documentation/arinote/211208_navigation_bar,_uiimageview,_nsmutableattributedstring,_addattribute.json @@ -0,0 +1 @@ +{"metadata":{"modules":[{"name":"Ari's Notes"}],"role":"article","roleHeading":"Article","title":"211208 Navigation Bar, UIImageView, NSMutableAttributedString, addAttribute"},"variants":[{"paths":["\/documentation\/arinote\/211208_navigation_bar,_uiimageview,_nsmutableattributedstring,_addattribute"],"traits":[{"interfaceLanguage":"swift"}]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"schemaVersion":{"minor":3,"patch":0,"major":0},"kind":"article","identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211208_Navigation_Bar,_UIImageView,_NSMutableAttributedString,_addAttribute"},"sections":[],"primaryContentSections":[{"content":[{"anchor":"TIL-Today-I-Learned","type":"heading","level":1,"text":"TIL (Today I Learned)"},{"inlineContent":[{"type":"text","text":"12월 8일 (수)"}],"type":"paragraph"},{"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"text":"학습 내용"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"만국박람회 STEP 2 진행","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"type":"heading","text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[Navigation Bar 감추기]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"스토리보드에서 감추기","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"네비게이션 컨트롤러를 클릭 후"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"우측 Inspector에서 Shows Navigation Bar 체크를 해제해준다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/8jpAm1w.png"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"코드로 감추기"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"네비게이션 컨트롤러로 Embed In 되어있는 ViewController 내부에 다음과 같은 코드를 추가해준다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"self.navigationController?.isNavigationBarHidden = true","type":"codeVoice"}]}]}]}]}]}]}]},{"inlineContent":[{"type":"text","text":"위와 같은 방법은 Navigation Controller에 연결되어있는 모든 ViewController의 Navigation Bar가 숨겨진다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"특정 ViewController의 Navigation Bar를 숨기려면 다음과 같은 방법이 있다."},{"type":"text","text":" "},{"text":"해당 방법은 View Life Cycle 메소드를 활용하여","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"뷰가 화면에 보일 때 Navigation Bar를 숨기고"},{"type":"text","text":" "},{"type":"text","text":"다른창으로 넘어갈때 다시 보이게하는 방법이다."}]},{"syntax":"swift","code":["override func viewWillAppear(_ animated: Bool) {"," super.viewWillAppear(animated)"," self.navigationController?.setNavigationBarHidden(true, animated: animated)","}","","override func viewWillDisappear(_ animated: Bool) {"," super.viewWillDisappear(animated)"," self.navigationController?.setNavigationBarHidden(false, animated: animated)","}"],"type":"codeListing"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Asset에 있는 이미지를 UIImageView에 적용하기]"}]}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"imageLiteral을 활용하여 설정하기","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/oe4a7HV.png"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"ImageView.image = #imageLiteral(resourceName: “image”)"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UIImage init으로 설정하기","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ImageView.image = UIImage(named: “image”)"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[Label의 특정 글자 크기 변경 방법]","type":"text"}]}],"type":"paragraph"},{"syntax":"swift","code":["\/\/ 내가 적용하고 싶은 폰트 사이즈","let fontSize = let fontSize = UIFont.systemFont(ofSize: 30)","","\/\/ label에 있는 Text를 NSMutableAttributedString로 초기화해준다.","guard let text = label.text else {"," return","}","let attributedString = NSMutableAttributedString(string: text)","","\/\/ 위에서 만든 attributedString에 addAttribute 메소드를 통해 Attribute를 적용해준다.","let range = (text as NSString).range(of: prefix) \/\/ 편집하고 싶은 글자의 범위를 구해준다.","attributedString.addAttribute(.font, value: fontSize, range: range)","","\/\/ 최종적으로 내 label에 속성을 적용한다.","label.attributedText = attributedString"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"위 방법을 적용하여 만든 함수","type":"text"}],"type":"paragraph"}]}]},{"syntax":"swift","code":["func editFontSize(of prefix: String ,in label: UILabel) {"," guard let text = label.text else {"," return"," }"," let fontSize = UIFont.systemFont(ofSize: label.font.pointSize + 3)"," let attributedString = NSMutableAttributedString(string: text)"," let range = (text as NSString).range(of: prefix)"," attributedString.addAttribute(.font, value: fontSize, range: range)"," label.attributedText = attributedString","}"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"사이즈 외에도 폰트나, 색상을 부분적으로 바꿔줄 수도 있다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"NSForegroundColorAttributeName","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSStrokeColorAttributeName"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"NSUnderlineStyleAttributeName","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"NSBackgroundColorAttributeName","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"NSStrikethroughStyleAttributeName"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/zeddios.tistory.com\/300","type":"text"}]}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"references":{"https://i.imgur.com/8jpAm1w.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/8jpAm1w.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/8jpAm1w.png"}]},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/oe4a7HV.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/oe4a7HV.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/oe4a7HV.png"}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211209_-----,_tableview,_xib,_celldidselect,_select-_------,_indexpathforselectedrow.json b/docs/data/documentation/arinote/211209_-----,_tableview,_xib,_celldidselect,_select-_------,_indexpathforselectedrow.json new file mode 100644 index 0000000..431a7a8 --- /dev/null +++ b/docs/data/documentation/arinote/211209_-----,_tableview,_xib,_celldidselect,_select-_------,_indexpathforselectedrow.json @@ -0,0 +1 @@ +{"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"211209 뷰의재사용, TableView, xib, celldidSelect, Select시 회색배경제거, indexPathForSelectedRow"},"sections":[],"schemaVersion":{"major":0,"minor":3,"patch":0},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211209_-----,_tableview,_xib,_celldidselect,_select-_------,_indexpathforselectedrow"]}],"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211209_-----,_TableView,_xib,_celldidSelect,_Select-_------,_indexPathForSelectedRow","interfaceLanguage":"swift"},"primaryContentSections":[{"content":[{"text":"TIL (Today I Learned)","level":1,"type":"heading","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"text":"12월 9일 (목)","type":"text"}]},{"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"text":"학습 내용"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"TableView의 재사용"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Auto Layout 강의"}]}]},{"content":[{"inlineContent":[{"text":"만국박람회 STEP 2 진행","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"취뽀 성공하신 2기분들의 QnA"}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"level":2,"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[뷰의 재사용, 활동학습 중…]"}]}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"cell을 dequeue하는 메소드에 복잡한 로직을 추가하는 것은 성능면에서 비효율적이기 때문에 지양한다."}]}]},{"content":[{"inlineContent":[{"text":"대신 셀이 Reuse Queue에 들어가 있을 때 로직을 추가하도록 한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"code":["\/\/ 여러가지 실험해본 예제코드","\/\/ 메소드 실행순서를 브레이크 포인트를 찍어가며 확인.","import UIKit","","class ViewController: UIViewController {"," var count = 0"," "," @IBOutlet var tableView: UITableView!"," "," override func viewDidLoad() {"," super.viewDidLoad()"," "," tableView.delegate = self"," tableView.dataSource = self"," }","}","","extension ViewController: UITableViewDelegate {","\/\/ func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {","\/\/ return 200","\/\/ }","}","extension ViewController: UITableViewDataSource {"," func numberOfSections(in tableView: UITableView) -> Int {"," print(#function) \/\/ [1] 셀을 가져오기전에 호출된다"," return 30"," }"," "," func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {"," print(#function, count)"," count += 1"," return 3 \/\/ [3] 섹션당 로우는 몇개인지"," }"," "," func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {"," print(#function, indexPath)"," let cell = tableView.dequeueReusableCell(withIdentifier: \"cell\", for: indexPath)"," var content = cell.defaultContentConfiguration()"," content.text = \"\\(indexPath.section), \\(indexPath.row)\""," cell.contentConfiguration = content"," "," if indexPath.row == 1 { \/\/ 재사용 셀을 확인해보는 코드"," cell.backgroundColor = .yellow"," }"," "," return cell \/\/ [4] 셀을 다 가져왔으면 화면에 보여줄 셀을 표시하기 시작한다."," }"," "," func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {"," print(#function)"," return \"Section \\(section)\" \/\/ [2] 셀을 가져올때 타이틀을 만들어준다, [5] 화면에 보여줄 섹션을 마지막에 불러온다."," }"," ","}","class MyCell: UITableViewCell {"," override func prepareForReuse() { \/\/ 큐에 들어가 있다면 색상을 바꾸삼"," \/\/ 이녀석도 스크롤 할때마다 호출되기는 하지만 우선순위는 낮다."," super.prepareForReuse()"," print(#function)"," self.backgroundColor = .white"," }","}"],"syntax":"swift","type":"codeListing"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Auto Layout]"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"최대한 View가 계산을 덜 하는 방향으로 제약을 잡아주는 것이 바람직하다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뷰 스스로 또는 뷰 사이의 관계를 속성을 통하여 정의한다. 제약은 방정식으로 나타낼 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/iXTxstM.png"}]}]},{"content":[{"inlineContent":[{"text":"제약조건을 주는 것이 어렵다면 글로 방정식을 적어보며 정리해보는 것도 방법이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"STEP 2를 진행하면서 지성과 함께 오토 레이아웃 삽질좀 해보았다. (원래는 STEP 3에서 하는거지만 못참겠어서…)"}]}]},{"content":[{"inlineContent":[{"text":"삽질하고 나니 왜 이 간단한걸 몇시간동안이나 삽질했는지 현타가 좀왔다…","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/Q1RhOD7.png","type":"image"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Horizontal 스택뷰를 만들고 안에 이미지뷰와 vertical 스택뷰를 추가한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"alignment를 center로, distribution은 fill spacing은 8"}]}]},{"content":[{"inlineContent":[{"text":"내부 이미지뷰의 너비를 vertical 스택뷰와 equal로 맞추고 Mutiplier를 0.2만큼 준다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이후 이미지뷰에 aspect를 1:1로 잡아준다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Horizontal 스택뷰의 top, bottom, leading, trailing을 Superview 기준으로 잡는다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"top, bottom은 12 만큼 띄어주고 leading, trailing은 15 만큼 띄어준다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"alignment, distribution은 fill, spacing은 2"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"스크롤뷰도 메인도 상세화면도 삽질의 연속이였다. 나의 부족함이 제일 큰 문제겠지..","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/VGagsPN.png","type":"image"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스크롤뷰의 제약은 슈퍼뷰와 top, bottom, leading, trailing을 Superview 기준으로 잡아주었다."}]}]},{"content":[{"inlineContent":[{"text":"이후 콘텐츠뷰인 스택뷰의 top, bottom, leading, trailing와 너비와 높이를 모두 제약을 걸어주니 해결되었다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"STEP 3때 또 삽질할 생각하니 정말 신난다.","type":"text"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"anchor":"129%EC%9D%BC%EC%9E%90-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A5%BC-%ED%95%98%EB%A9%B4%EC%84%9C-%EC%96%BB%EC%97%88%EB%8D%98-%EC%A7%80%EC%8B%9D","text":"12\/9일자 프로젝트를 하면서 얻었던 지식","level":3,"type":"heading"},{"inlineContent":[{"inlineContent":[{"text":"[셀이 didSelect 되었을 때 화면 이동하기]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/HkWjrjZ.png","type":"image"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"셀의 악세사리 ","type":"text"},{"code":"Disclosure Indicator","type":"codeVoice"},{"type":"text","text":"를 추가"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/hiouNGu.png"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"테이블 뷰의 Selection을 "},{"type":"codeVoice","code":"Single Selection"},{"text":"으로 지정해준다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/I0lrhAg.gif","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스토리보드에서 "},{"code":"Segue","type":"codeVoice"},{"text":"를 생성해주고,","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/AVg2X23.png","type":"image"}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Identifier"},{"type":"text","text":"를 지정해준다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"이후 코드로 돌아가서 Delegate 프로토콜을 활용한다","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"셀이 눌렸을 때 호출되는 메소드를 호출하고, performSegue를 활용하여 화면전환을 한다."}]}]}]}]}],"type":"unorderedList"},{"code":["extension ItemListViewController: UITableViewDelegate {"," func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {"," performSegue(withIdentifier: \"ItemDetailView\", sender: nil)"," }","}"],"syntax":"swift","type":"codeListing"},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[셀이 Select되었을 때 남는 회색배경 없애기]"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/8aTrGCb.gif","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"셀을 클릭해서 화면전환후 다시 돌아와도 셀의 배경색이 안없어지는 현상이 일어났다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테이블뷰의 셀이 선택되면 회색으로 변하게 되는데, 문제는 이렇게 변한 색이 그대로 유지된다."}]}]},{"content":[{"inlineContent":[{"text":"테이블 뷰의 셀을 선택하면 회색으로 변했다가 다시 원래 색으로 바로 돌아오게 하려면 아래 메소드를 사용해주면 된다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["extension ItemListViewController: UITableViewDelegate {"," func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {"," tableView.deselectRow(at: indexPath, animated: true)"," }","}"]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"적용후","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/BNkoL1h.gif","type":"image"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[xib로 만든 TableViewCell을 TableView에 등록하는 방법]"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Cocoa touch class를 생성시 xib도 동시에 생성할 수 있다."}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/nwz8zTx.png","type":"image"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이런 xib로 만들어진 cell을 TableView에 등록해주려면 어떻게 해야할까?","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/6D8WV11.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"먼저 Custom Cell의 Identifier를 설정해준다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/IOU3yAx.png"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그리고 TableView를 IBOutlet으로 추가한 ViewController를 열어서 아래 코드를 viewDidLoad에 추가해주면 된다."}],"type":"paragraph"}]}]},{"type":"codeListing","code":["private func registerXib() {"," let nibName = UINib(nibName: \"CustomTableViewCell\", bundle: nil)"," tableView.register(nibName, forCellReuseIdentifier: \"customCell\")","}"],"syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이후 tableView의 DataSource와 Delegate를 사용하여 셀의 데이터를 추가해주면 된다."}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[선택한 셀의 indexPath를 알아내는 방법]"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"prepare에서 선택한 셀의 indexpath를 알아야했다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"다음화면에 선택한 셀의 정보를 바로 넘겨주기 위함."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"TableView 속성을 찾아보니 선택된 셀의 indexpath를 반환하는 프로퍼티가 있었다."}]}]}]},{"type":"codeListing","syntax":"swift","code":["var indexPathForSelectedRow: IndexPath? { get }"]},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"text":"[화면 전환 시 데이터를 전달하는 두가지 방법]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"performSegue시 sender를 nil이 아니라 전달할 데이터를 입력후","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"prepare시 sender를 다운캐스팅을 활용하여 전달.","type":"text"}]}]}],"type":"unorderedList"},{"type":"codeListing","code":["extension ItemListViewController: UITableViewDelegate {"," func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {"," performSegue(withIdentifier: \"ItemDetailView\", sender: items[indexPath.row])"," tableView.deselectRow(at: indexPath, animated: true)"," }","}","","override func prepare(for segue: UIStoryboardSegue, sender: Any?) {"," guard let item = sender as? ExpositionItem,"," let nextViewController = segue.destination as? ItemDetailViewController else {"," return"," }"," nextViewController.item = item","}"],"syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"prepare 메소드 내부에서 선택된 셀의 indexpath를 구해서 전달하는 방법."}]}]}]},{"type":"codeListing","syntax":"swift","code":["extension ItemListViewController: UITableViewDelegate {"," func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {"," performSegue(withIdentifier: \"ItemDetailView\", sender: nil)"," tableView.deselectRow(at: indexPath, animated: true)"," }","}","","override func prepare(for segue: UIStoryboardSegue, sender: Any?) {"," guard let indexPath = itemListTableVIew.indexPathForSelectedRow,"," let nextViewController = segue.destination as? ItemDetailViewController else {"," return"," }"," nextViewController.item = items[indexPath.row]","}"]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[2기분들의 취뽀 후기]","type":"text"}]}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"취뽀하신 2기분들의 후기를 들어보니 캠프를 열심히 하면 되겠다는 생각이 들었다. (활동학습, 기록 등등..)","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"최신 기술도 좋지만 기본기부터 탄탄하게 다지는 것이 중요하다고 느껴졌다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"README는 결국에 바쁜 면접관들이 보는 것이니 길게 적을 필요없다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"핵심적인 내용 위주로 현업에서 자주 쓰이는 것을 집중해서 작성하고, 차별화를 두는 것에 집중하자."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"캠프를 수료 후 기존에 했던 프로젝트를 변형해보는 것도 방법이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"돈이 최고다."}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/baked-corn.tistory.com\/124"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/m.blog.naver.com\/PostView.naver?isHttpsRedirect=true&blogId=jdub7138&logNo=220963701224","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableview\/1614989-deselectrow"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableview\/1615000-indexpathforselectedrow","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/leeari95.tistory.com\/54","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/leeari95.tistory.com\/59","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}],"kind":"content"}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"references":{"https://i.imgur.com/hiouNGu.png":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/hiouNGu.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/hiouNGu.png"},"https://i.imgur.com/6D8WV11.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/6D8WV11.png","variants":[{"url":"https:\/\/i.imgur.com\/6D8WV11.png","traits":["1x","light"]}],"alt":null},"https://i.imgur.com/BNkoL1h.gif":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/BNkoL1h.gif"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/BNkoL1h.gif"},"https://i.imgur.com/HkWjrjZ.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/HkWjrjZ.png","variants":[{"url":"https:\/\/i.imgur.com\/HkWjrjZ.png","traits":["1x","light"]}],"alt":null},"https://i.imgur.com/8aTrGCb.gif":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/8aTrGCb.gif"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/8aTrGCb.gif"},"https://i.imgur.com/iXTxstM.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/iXTxstM.png","variants":[{"url":"https:\/\/i.imgur.com\/iXTxstM.png","traits":["1x","light"]}],"alt":null},"https://i.imgur.com/I0lrhAg.gif":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/I0lrhAg.gif"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/I0lrhAg.gif"},"https://i.imgur.com/VGagsPN.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/VGagsPN.png","variants":[{"url":"https:\/\/i.imgur.com\/VGagsPN.png","traits":["1x","light"]}],"alt":null},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/AVg2X23.png":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/AVg2X23.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/AVg2X23.png"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/IOU3yAx.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/IOU3yAx.png","variants":[{"url":"https:\/\/i.imgur.com\/IOU3yAx.png","traits":["1x","light"]}],"alt":null},"https://i.imgur.com/nwz8zTx.png":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/nwz8zTx.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/nwz8zTx.png"},"https://i.imgur.com/Q1RhOD7.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/Q1RhOD7.png","variants":[{"url":"https:\/\/i.imgur.com\/Q1RhOD7.png","traits":["1x","light"]}],"alt":null}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211210_loadviewifneeded,_storyboard_reference.json b/docs/data/documentation/arinote/211210_loadviewifneeded,_storyboard_reference.json new file mode 100644 index 0000000..377991e --- /dev/null +++ b/docs/data/documentation/arinote/211210_loadviewifneeded,_storyboard_reference.json @@ -0,0 +1 @@ +{"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211210_loadviewifneeded,_Storyboard_Reference","interfaceLanguage":"swift"},"primaryContentSections":[{"kind":"content","content":[{"level":1,"type":"heading","text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned"},{"inlineContent":[{"type":"text","text":"12월 10일 (금)"}],"type":"paragraph"},{"type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"만국박람회 STEP 2 리팩토링 후 PR 작성"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"loadviewifneeded()"},{"type":"text","text":"의 용도?"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"code":"Storyboard Reference","type":"codeVoice"},{"text":"가 뭘까?","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","type":"heading","text":"고민한 점 \/ 해결 방법"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[loadviewifneeded]","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/aCfGAKg.png","type":"image"}]},{"items":[{"content":[{"inlineContent":[{"text":"제이티와 쥬스메이커 때 사용해봤던 메소드.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"나무와 숲재가 뷰를 전환하기 전에 loadView를 사용해서 미리 다음 화면의 View를 load하고 있는 것을 보고 이전에 피드백 받았던 내용이 떠올라서 공유해보았다."}]}]},{"content":[{"inlineContent":[{"text":"얘기를 나누다가 찾다보니까 발견하게 되었는데, 이 메소드가 알고보니 ViewController를 테스트할 때 주로 사용되는 메소드였다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"나무, 숲재, 차차와 이야기를 나누는 도중에 발견한…. 이게모야..?","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/ZXTOLzp.png","type":"image"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Storyboard Reference]"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"Storyboard 장점"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ViewController 간 연간 관계를 한눈에 볼 수 있다"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ViewController의 형태를 시각적으로 표현해줘서 UI가 어떻게 생겼는지 확인할 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"컨트롤 생성 시 Drag & Drop으로 간단히 생성할 수 있다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"Storyboard 단점"}],"type":"strong"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Git Marge 시 Storyboard에서 충돌이 발생함","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"스토리보드에 여러개의 ViewController가 추가되면 스토리보드 실행이 느려지고 심지어 Xcode가 멈춰버리는 일도 발생한다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이러한 문제를 해결하기 위해 2가지 방법을 많이 사용하게 된다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스토리보드를 사용하지 않고 100% 코드로 UI 개발"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"One Storyboard"},{"type":"text","text":", "},{"type":"codeVoice","code":"One ViewController"},{"text":"(하나의 스토리보드에 하나의 뷰 컨트롤러만) = 스토리보드 나누기","type":"text"}]}]}]}]}]},{"inlineContent":[{"type":"text","text":"따라서 Storyboard Reference는 스토리보드를 나눌 때, Tabbar를 사용할 때 주로 활용할 수 있다."}],"type":"paragraph"},{"type":"thematicBreak"},{"type":"heading","text":"스토리보드 래퍼런스를 활용하여 기존 스토리보드 분할해보기","anchor":"%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B3%B4%EB%93%9C-%EB%9E%98%ED%8D%BC%EB%9F%B0%EC%8A%A4%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%98%EC%97%AC-%EA%B8%B0%EC%A1%B4-%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B3%B4%EB%93%9C-%EB%B6%84%ED%95%A0%ED%95%B4%EB%B3%B4%EA%B8%B0","level":3},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"연결되어있는 3개의 ViewController를 나눠보자.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/em7vnDz.png","type":"image"}]}]},{"content":[{"inlineContent":[{"text":"먼저 스토리보드 파일을 만들어준다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/mUIjBJP.png","type":"image"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그리고 CMD + X, CMD + V를 활용하여 각 파일에 Controller를 넣어준다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/LuXiBFM.gif","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이후 스토리보드 ID와 Is Initial View Controller를 체크해준다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/AjeNZXf.png"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/FkJb2sV.png","type":"image"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"체크를 해주지 않으면 에러가 발생하니까 꼭 해준다."}]}]},{"content":[{"inlineContent":[{"text":"나머지 뷰로 동일한 방법으로 분할해준다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/2wjAblK.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"메인으로 돌아가서 스토리보드 래퍼런스를 추가해준다."}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/cdPBtyb.gif","type":"image"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"그리고 아래처럼 매칭시킬 스토리보드를 지정해주고","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ID는 매칭할 ViewControllet의 Storyboard ID와 동일하게 맞춰준다."}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/YYNKKmR.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그리고 Segue를 생성해준다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/wslBc2G.gif"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Segue를 활용하여 화면전환을 할 경우 아래처럼 연결해주고 Segue의 Identifier를 지정해주면 된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/fKU68CG.gif"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"적용후 정상적으로 Segue를 타고 화면 전환을 하는 모습을 확인할 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/9kHr778.gif","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"요약"}]},{"items":[{"content":[{"inlineContent":[{"text":"새로운 ","type":"text"},{"type":"codeVoice","code":"Storyboard"},{"type":"text","text":" 파일을 생성"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컨트롤러를 새로 만든 Storyboard 파일에 분할해준다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"분할해준 컨트롤러의 "},{"type":"codeVoice","code":"Storyboard ID"},{"type":"text","text":" 설정과 "},{"code":"Is Initial View Controller","type":"codeVoice"},{"text":" 체크!!!!","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 스토리보드 래퍼런스를 추가한 후 "},{"type":"codeVoice","code":"Storyboard"},{"type":"text","text":", "},{"type":"codeVoice","code":"Referenced ID"},{"type":"text","text":"를 설정한다."}]}]},{"content":[{"inlineContent":[{"code":"Segue","type":"codeVoice"},{"type":"text","text":"를 연결한다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"items":[{"content":[{"inlineContent":[{"text":"https:\/\/felix-mr.tistory.com\/15","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/github.com\/leeari95\/ios-juice-maker\/tree\/4-leeari95#3-2-%EC%9D%98%EB%AC%B8%EC%A0%90"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"sections":[],"kind":"article","schemaVersion":{"minor":3,"major":0,"patch":0},"metadata":{"role":"article","title":"211210 loadviewifneeded, Storyboard Reference","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211210_loadviewifneeded,_storyboard_reference"]}],"references":{"https://i.imgur.com/mUIjBJP.png":{"identifier":"https:\/\/i.imgur.com\/mUIjBJP.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/mUIjBJP.png"}],"alt":null,"type":"image"},"https://i.imgur.com/9kHr778.gif":{"identifier":"https:\/\/i.imgur.com\/9kHr778.gif","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/9kHr778.gif"}],"alt":null,"type":"image"},"https://i.imgur.com/LuXiBFM.gif":{"identifier":"https:\/\/i.imgur.com\/LuXiBFM.gif","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/LuXiBFM.gif"}],"alt":null,"type":"image"},"https://i.imgur.com/AjeNZXf.png":{"identifier":"https:\/\/i.imgur.com\/AjeNZXf.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/AjeNZXf.png"}],"alt":null,"type":"image"},"https://i.imgur.com/FkJb2sV.png":{"identifier":"https:\/\/i.imgur.com\/FkJb2sV.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/FkJb2sV.png"}],"alt":null,"type":"image"},"https://i.imgur.com/2wjAblK.png":{"identifier":"https:\/\/i.imgur.com\/2wjAblK.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/2wjAblK.png"}],"alt":null,"type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/em7vnDz.png":{"identifier":"https:\/\/i.imgur.com\/em7vnDz.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/em7vnDz.png"}],"alt":null,"type":"image"},"https://i.imgur.com/ZXTOLzp.png":{"identifier":"https:\/\/i.imgur.com\/ZXTOLzp.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/ZXTOLzp.png"}],"alt":null,"type":"image"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/aCfGAKg.png":{"identifier":"https:\/\/i.imgur.com\/aCfGAKg.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/aCfGAKg.png"}],"alt":null,"type":"image"},"https://i.imgur.com/YYNKKmR.png":{"identifier":"https:\/\/i.imgur.com\/YYNKKmR.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/YYNKKmR.png"}],"alt":null,"type":"image"},"https://i.imgur.com/cdPBtyb.gif":{"identifier":"https:\/\/i.imgur.com\/cdPBtyb.gif","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/cdPBtyb.gif"}],"alt":null,"type":"image"},"https://i.imgur.com/wslBc2G.gif":{"identifier":"https:\/\/i.imgur.com\/wslBc2G.gif","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/wslBc2G.gif"}],"alt":null,"type":"image"},"https://i.imgur.com/fKU68CG.gif":{"identifier":"https:\/\/i.imgur.com\/fKU68CG.gif","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/fKU68CG.gif"}],"alt":null,"type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211213_accessibility,_dynamic_type,_voice_over,_fatalerror,_---_-----,_vim.json b/docs/data/documentation/arinote/211213_accessibility,_dynamic_type,_voice_over,_fatalerror,_---_-----,_vim.json new file mode 100644 index 0000000..8ab2df7 --- /dev/null +++ b/docs/data/documentation/arinote/211213_accessibility,_dynamic_type,_voice_over,_fatalerror,_---_-----,_vim.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Xcode"]]},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211213_Accessibility,_Dynamic_Type,_Voice_over,_fatalError,_---_-----,_Vim"},"kind":"article","variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211213_accessibility,_dynamic_type,_voice_over,_fatalerror,_---_-----,_vim"]}],"metadata":{"title":"211213 Accessibility, Dynamic Type, Voice over, fatalError, 함수형 프로그래밍, Vim","modules":[{"name":"Ari's Notes"}],"role":"article","roleHeading":"Article"},"schemaVersion":{"patch":0,"major":0,"minor":3},"primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","level":1,"type":"heading","text":"TIL (Today I Learned)"},{"inlineContent":[{"type":"text","text":"12월 13일 (월)"}],"type":"paragraph"},{"text":"학습 내용","type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Accessibility 활동학습","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"fatalError의 용도?","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"함수형 프로그래밍 복습","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Vim이란?"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2,"text":"고민한 점 \/ 해결 방법","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[fatalError]"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"호출 즉시 크래시를 발생 시킨다 (프로세스를 죽임)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"언제?","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"에러가 치명적일 때"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"메소드에서 리턴할 것이 없을 때"}]}]}],"type":"unorderedList"}]},{"content":[{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"주로 tableView의 customcell을 다운캐스팅으로 옵셔널바인딩을 할 때 사용하기도 한다."}]}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[함수형 프로그래밍]","type":"text"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"특징"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"인풋과 아웃풋이 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"외부 환경으로부터 철저히 독립적이다"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"같은 인풋에 있어서 언제나 동일한 아웃풋을 생산해낸다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"함수형 프로그래밍이 주목받게 된 주요 이유 중 하나는 ‘부작용’에 의한 문제로부터 보다 자유롭다는 것이다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"부작용이란?"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"어떤 함수의 동작에 의해 프로세스 내 특정 상태가 변경되는 상황을 말한다."}]}]}]}]}]},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Accesibility]"}]}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"접근성"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"접근하기 쉬운 성질, 사물이나 환경 따위를 사용자가 불편함없이 이용할 수 있는 정도"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"접근성 지원"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"누군가의 개성, 문화, 현재 상태(건강 및 주변환경)에 무관하게 앱을 편히 쓸 수 있게 지원하는 것"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"장애인과 비장애인의 차이에 따른 지원이 아님"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Vision, Gearing, Mobility, Cognitive"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"시각적","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Dynamic Type. Voice Over, 대비증가, 애니메이션 줄이기 등"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"청각적","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"화재 및 초인종 등의 소리를 감지해 화면에 표시하는 소리인식 기능, 자막 등","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"운동 능력","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"받아쓰기, 자동완성, 뒷면 탭 등","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"인지 능력"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Safari 읽기도구 사용법 유도 기능"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Xcode → Open Developer Tool → Accesibility Inspector"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"해당 경로에 Audit 탭을 활용하면 어떤 부분이 문제점이 있는지 확인할 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/YBhQgMa.png"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Dynamic Type]"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"모든 사용자가 시력이 같진 않다. 어떤 사용자는 글씨를 크게 키워서 앱을 이용할 수도 있는데, 우리가 앱을 개발할 때 font 크기를 지정해버리면 사용자가 아무리 글씨를 크게 키운다고 해도 앱의 font 크기는 그대로일 것이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그래서 그런 요구사항을 유연하게 충족할 수 있도록 Dynamic Type을 사용한다면 훨씬 접근성을 구현하기가 좋아질 수 있겠다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"Dynamic Type을 적용하려면?"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"폰트 크기를 System이 아니라 Dynamic Type으로 설정해준다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/7ntEh1o.png","type":"image"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"Automatically Adjusts Font"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"앱이 실행되는 동안에 글씨 크기가 바꾸는 것을 허용하는지의 대한 것"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"체크를 해주지 않으면 앱을 종료하고 다시 실행시켜서 폰트가 커졌는지 확인해야하는 번거로움이 생기기 때문에 체크를 해주는 것이 바람직하다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Label은 스토리보드의 Inspector에서 적용할 수 있지만 버튼의 경우는 불가하기 때문에 코드로 직접 설정해주어야 한다.","type":"text"}]}]}]},{"code":["button.titleLabel?.adjustsFontForContentSizeCategory = true"],"syntax":"swift","type":"codeListing"}]}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[Voice over]","type":"text"}]},{"text":" ","type":"text"},{"text":"화면을 보지 않고도 사용자가 장치를 제어할 수 있게 해주는 Apple의 혁신적인 화면 읽기 기술이다. 앱의 객체와 상호 작용하는 스크린 리더로, 사용자가 볼 수 없더라도 인터페이스를 사용할 수 있다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Voice over 기능을 통해 사용자가 잘 알아듣도록 읽게 하려면?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"각 요소에 모두 Accessibility Label을 설정해야 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UI 요소 타입은 자동으로 읽어주기 때문에 이름에 포함하지 않는다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"code":"Add button","type":"codeVoice"},{"type":"text","text":" Not Good"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"Add","type":"codeVoice"},{"type":"text","text":" Good"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"아래 +와 같은 버튼의 Accessibility Label은 단순히 Add라 짓지 않고 어떤 것의 Add인지 자세히 설명해준다."}]},{"items":[{"content":[{"inlineContent":[{"code":"Add","type":"codeVoice"},{"text":" Not Good","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"Add Peanut Butter","type":"codeVoice"},{"text":" Good","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"반복된 단어의 사용은 지양한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"예를 들어 음악 재생 애플리케이션에 있는 버튼의 Accessibility Label은 당연히 음악과 관련된 버튼일 것이므로 song, music과 같은 이름을 생략한다."}]},{"items":[{"content":[{"inlineContent":[{"code":"Previous song, Play song, Next song","type":"codeVoice"},{"text":" Not Good","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Previous, Play, Next"},{"text":" Good","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"애니메이션에 의미 있는 이름을 지어준다."}]},{"items":[{"content":[{"inlineContent":[{"code":"spinner","type":"codeVoice"},{"type":"text","text":" Not Good"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"`Loading…`` Good","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"너무 장황한 설명은 지양한다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Delete item from the current folder and add it to the trash"},{"type":"text","text":" Not Good"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Delete"},{"type":"text","text":" Good"}]}]}]}]},{"content":[{"inlineContent":[{"text":"적절한 상황에는 장황한 이름을 지어도 좋다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"예를 들어, 이모티콘과 같은 요소는 느낌과 감정을 표현하기 위해 약간은 긴 설명이 포함되어도 좋다."}],"type":"paragraph"}]}]}]}]}]},{"content":[{"inlineContent":[{"text":"시스템 언어를 바꾸면 한글이여도 해당 언어로 번역하여 voice over를 실행시켜준다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[Vim이란?]"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"마우스 없이 단축키를 이용하여 코드를 편집할 수 있는 프로그램이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Xcode에서 Vim 모드 설정하기"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/WckyZl8.png","type":"image"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Vim의 간단한 단축키 모음"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Up9GMP7.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"단축키모드와 입력모드","type":"text"}]}]}]},{"rows":[[[{"inlineContent":[{"type":"text","text":"단축키"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"설명"}]}]],[[{"inlineContent":[{"type":"codeVoice","code":"i"}],"type":"paragraph"}],[{"inlineContent":[{"type":"text","text":"커서 앞에서 입력모드 시작"}],"type":"paragraph"}]],[[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"I"}]}],[{"inlineContent":[{"type":"text","text":"줄 맨앞에서 입력모드 시작"}],"type":"paragraph"}]],[[{"inlineContent":[{"type":"codeVoice","code":"a"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"커서 뒤에서 입력모드 시작"}]}]],[[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"A"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"줄 맨뒤에서 입력모드 시작"}]}]],[[{"inlineContent":[{"code":"ESC","type":"codeVoice"}],"type":"paragraph"}],[{"inlineContent":[{"text":"입력모드에서 단축키모드로 전환","type":"text"}],"type":"paragraph"}]]],"header":"row","alignments":["center","left"],"type":"table"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"커서 이동"}]}]}],"type":"unorderedList"},{"type":"table","header":"row","alignments":["center","left"],"rows":[[[{"type":"paragraph","inlineContent":[{"type":"text","text":"단축키"}]}],[{"type":"paragraph","inlineContent":[{"text":"설명","type":"text"}]}]],[[{"inlineContent":[{"type":"codeVoice","code":"h"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"text":"👈🏻","type":"text"}]}]],[[{"type":"paragraph","inlineContent":[{"code":"j","type":"codeVoice"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"👇🏻"}]}]],[[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"k"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"👆🏻"}]}]],[[{"inlineContent":[{"code":"l","type":"codeVoice"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"👉🏻"}]}]],[[{"inlineContent":[{"code":"w","type":"codeVoice"}],"type":"paragraph"}],[{"inlineContent":[{"text":"다음 단어로","type":"text"}],"type":"paragraph"}]],[[{"inlineContent":[{"type":"codeVoice","code":"b"}],"type":"paragraph"}],[{"inlineContent":[{"type":"text","text":"이전 단어로"}],"type":"paragraph"}]],[[{"type":"paragraph","inlineContent":[{"code":"{","type":"codeVoice"}]}],[{"type":"paragraph","inlineContent":[{"text":"문단 시작으로","type":"text"}]}]],[[{"inlineContent":[{"code":"}","type":"codeVoice"}],"type":"paragraph"}],[{"inlineContent":[{"text":"문단 끝으로","type":"text"}],"type":"paragraph"}]]]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"복사, 붙여넣기, 선택모드"}],"type":"paragraph"}]}]},{"rows":[[[{"inlineContent":[{"type":"text","text":"단축키"}],"type":"paragraph"}],[{"inlineContent":[{"type":"text","text":"설명"}],"type":"paragraph"}]],[[{"inlineContent":[{"type":"codeVoice","code":"v"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"text":"선택모드","type":"text"}]}]],[[{"inlineContent":[{"type":"codeVoice","code":"V"}],"type":"paragraph"}],[{"inlineContent":[{"type":"text","text":"줄 단위 선택모드"}],"type":"paragraph"}]],[[{"inlineContent":[{"type":"codeVoice","code":"y"}],"type":"paragraph"}],[{"inlineContent":[{"type":"text","text":"복사"}],"type":"paragraph"}]],[[{"inlineContent":[{"type":"codeVoice","code":"p"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"붙여넣기"}]}]],[[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"x"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"선택 부분 오려두기"}]}]],[[{"inlineContent":[{"code":"d","type":"codeVoice"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"text":"선택 부분 지우기","type":"text"}]}]]],"header":"row","alignments":["center","left"],"type":"table"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"수정모드, 내어쓰기, 들여쓰기","type":"text"}]}]}],"type":"unorderedList"},{"type":"table","header":"row","rows":[[[{"inlineContent":[{"text":"단축키","type":"text"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"text":"설명","type":"text"}]}]],[[{"inlineContent":[{"type":"codeVoice","code":"R"}],"type":"paragraph"}],[{"inlineContent":[{"text":"수정모드","type":"text"}],"type":"paragraph"}]],[[{"inlineContent":[{"type":"codeVoice","code":"r"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"text":"한 글자 수정","type":"text"}]}]],[[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"o"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"빈 줄 넣고 입력모드"}]}]],[[{"inlineContent":[{"type":"codeVoice","code":"<"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"text":"내어쓰기","type":"text"}]}]],[[{"inlineContent":[{"type":"codeVoice","code":">"}],"type":"paragraph"}],[{"inlineContent":[{"type":"text","text":"들여쓰기"}],"type":"paragraph"}]]],"alignments":["center","left"]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"화면 이동"}],"type":"paragraph"}]}]},{"alignments":["center","left"],"rows":[[[{"inlineContent":[{"type":"text","text":"단축키"}],"type":"paragraph"}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"설명"}]}]],[[{"type":"paragraph","inlineContent":[{"code":"H","type":"codeVoice"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"화면 맨 위로 이동"}]}]],[[{"type":"paragraph","inlineContent":[{"code":"L","type":"codeVoice"}]}],[{"inlineContent":[{"type":"text","text":"화면 맨 아래 이동"}],"type":"paragraph"}]],[[{"type":"paragraph","inlineContent":[{"code":"M","type":"codeVoice"}]}],[{"type":"paragraph","inlineContent":[{"text":"화면 한 가운데 이동","type":"text"}]}]],[[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Ctrl + u"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"반 페이지씩 화면 위로 이동"}]}]],[[{"inlineContent":[{"code":"Ctrl + d","type":"codeVoice"}],"type":"paragraph"}],[{"inlineContent":[{"type":"text","text":"반 페이지씩 화면 아래로 이동"}],"type":"paragraph"}]]],"type":"table","header":"row"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/gwangyonglee.tistory.com\/52"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@ryan-son\/Xcode-Accessibility-Accessibility-Inspector"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/eunjin3786.tistory.com\/366"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/www.youtube.com\/watch?v=jVG5jvOzu9Y"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/www.youtube.com\/watch?v=qn1soztN7k4"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/github.com\/johngrib\/simple_vim_guide","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/vim-adventures.com\/"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/www.sungdoo.dev\/programming\/why-xcode-supporthttps:\/\/vim-adventures.com\/s-vim"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]}],"sections":[],"references":{"doc://AriNote/documentation/AriNote/Xcode":{"role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Xcode","url":"\/documentation\/arinote\/xcode","title":"Xcode","kind":"article","abstract":[{"text":"언젠가…. 좋아질거지 Xcode…?","type":"text"}],"type":"topic"},"https://i.imgur.com/Up9GMP7.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/Up9GMP7.png"}],"identifier":"https:\/\/i.imgur.com\/Up9GMP7.png","alt":null,"type":"image"},"https://i.imgur.com/YBhQgMa.png":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/YBhQgMa.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/YBhQgMa.png"}]},"https://i.imgur.com/7ntEh1o.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/7ntEh1o.png"}],"identifier":"https:\/\/i.imgur.com\/7ntEh1o.png","alt":null,"type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/WckyZl8.png":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/WckyZl8.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/WckyZl8.png"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211214_static,_factory_pattern,_visibleviewcontroller.json b/docs/data/documentation/arinote/211214_static,_factory_pattern,_visibleviewcontroller.json new file mode 100644 index 0000000..12177ef --- /dev/null +++ b/docs/data/documentation/arinote/211214_static,_factory_pattern,_visibleviewcontroller.json @@ -0,0 +1 @@ +{"schemaVersion":{"patch":0,"minor":3,"major":0},"variants":[{"paths":["\/documentation\/arinote\/211214_static,_factory_pattern,_visibleviewcontroller"],"traits":[{"interfaceLanguage":"swift"}]}],"sections":[],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211214_static,_Factory_Pattern,_visibleViewController","interfaceLanguage":"swift"},"metadata":{"modules":[{"name":"Ari's Notes"}],"title":"211214 static, Factory Pattern, visibleViewController.md","role":"article","roleHeading":"Article"},"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"primaryContentSections":[{"kind":"content","content":[{"level":1,"type":"heading","text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"text":"12월 14일 (화)","type":"text"}]},{"text":"학습 내용","type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"enum의 static","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Factory Pattern"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"topViewController와 visibleViewController"},{"type":"text","text":" "},{"type":"text","text":" "}],"type":"paragraph"}]}],"type":"unorderedList"},{"text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2,"type":"heading"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[Enum의 static 메소드, 혹은 static 변수]"}],"type":"strong"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"static 같은 경우에는 메모리에 data 영역에 존재한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"data영역은 프로그램이 실행하고 끝날 때 까지 데이터가 죽지않고 살아남아있다."}]}]},{"content":[{"inlineContent":[{"text":"따라서 사용여부와 상관 없이 항상 메모리가 살아있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"만국박람회 프로젝트 중에 enum을 활용하여 static 메소드와 static 변수를 묶어둔 타입들이 존재하는데, 이를 어떻게 해결하면 좋을지 고민해보자"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Factory Pattern]"}]}]},{"items":[{"content":[{"inlineContent":[{"text":"Factory Pattern이란?","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"객체를 만들기 위한 프로토콜을 정의하지만, 어떤 클래스의 인스턴스를 생성할지에 대한 결정은 하위 클래스가 정하도록 하는 방법"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"객체 생성을 서브 클래스가 하도록 처리하는 패턴"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉, 객체 생성을 캡슐화할 수 있으며, 이로 인해 부모 클래스는 자식 클래스가 어떤 객체를 생성하는지 몰라도 된다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"구조","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Product"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Creator와 하위 클래스가 생성할 수 있는 모든 객체에 동일한 프로토콜를 선언한다","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"Concreate Product","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Product가 선언한 프로토콜로 만든 실제 객체다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Creator"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"새로운 객체를 반환하는 팩토리 메서드를 선언한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"여기서 반환하는 객체는 Product 프로토콜을 준수하고 있어야 한다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Concreate Creator"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"기본 팩토리 메소드를 override 하여 서로 다른 Product 객체를 만든다."}],"type":"paragraph"}]}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"언제 사용할까??"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"만들 객체의 클래스 종류를 예측할 수 없을 때","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"만들어야 할 객체의 하위 클래스를 명시하고 싶을 때","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"종류","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Factory Method Pattern","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Abstract Factory Pattern","type":"text"}]}]}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[topViewController와 visibleViewController는 같은 VC을 가르키는 걸까?]","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/ghcxKC0.jpg"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"위 그림을 보면 topViewController와 visibleViewController가 맨 앞에 같은 VC을 가르키고 있다."}]}]},{"content":[{"inlineContent":[{"text":"하지만 topViewController와 visibleViewController는 반드시 같은 것은 아니다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"예를 들어, 하나의 VC를 모달창으로 나타낸다면 visibleViewController는 모달 VC을 가르킬 것이고, topViewController는 변하지 않는다.","type":"text"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/theswiftdev.com\/comparing-factory-design-patterns\/","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/daheenallwhite.github.io\/design%20pattern\/2019\/05\/07\/Factory-Pattern\/"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/velog.io\/@ryan-son\/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-Factory-pattern-in-Swift","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/kingso.netlify.app\/posts\/boostcourse-view-controller\/","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/stackoverflow.com\/questions\/33395463\/in-uinavigationcontroller-what-is-the-difference-between-topviewcontroller-visi"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}],"references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/ghcxKC0.jpg":{"type":"image","identifier":"https:\/\/i.imgur.com\/ghcxKC0.jpg","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/ghcxKC0.jpg"}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211215_-----_------.json b/docs/data/documentation/arinote/211215_-----_------.json new file mode 100644 index 0000000..b1894c6 --- /dev/null +++ b/docs/data/documentation/arinote/211215_-----_------.json @@ -0,0 +1 @@ +{"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211215_-----_------"]}],"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","type":"heading","level":1,"anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"type":"text","text":"12월 15일 (수)"}]},{"type":"heading","level":2,"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"운영체제"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"데이터베이스","type":"text"},{"text":" ","type":"text"},{"type":"text","text":" "}]}]}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2,"text":"고민한 점 \/ 해결 방법","type":"heading"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[운영체제]","type":"text"}]}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/uQbcd8n.png","type":"image"}]}]},{"content":[{"inlineContent":[{"text":"컴퓨터의 시스템 소프트웨어 종류중의 한 종류라고 볼 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"하드웨어와 응용프로그램 사이의 매개체 역할"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"종류","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"Linux","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"macOS"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Windows","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"iOS"}]}]},{"content":[{"inlineContent":[{"text":"Android","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Unix…"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"목적"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"다양한 종류의 컴퓨터가 나오게 되었고 여러가지 일을 처리해야할 필요성이 생기게 되었다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"다양한 종류의 하드웨어에서 사용자와 연결지어줄 고리들이 필요하게 되었다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"태블릿, 노트북, 데스크탑 여러가지 하드웨어에서 범용적으로 사용할 수 있는 매개체, 소프트웨어가 필요하게 되었다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그래서 운영체제라는 것이 생기게 되었다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/tTPJnj2.png","type":"image"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"mac은 유닉스를 모티브로 만들어졌다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"windows는 독자적인 길을 걷고 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"리눅스는 운영체제라기 보다 커널이라는 뼈대의 이름이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"iOS도 macOS의 뼈대를 이어받은 운영체제라고 볼 수 있다"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"운영체제는 크게 3가지 일을 할 수 있다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"시스템 하드웨어 관리"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"사용자 프로그램의 오류나 잘못된 자원 사용을 감시","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"입출력 장치 등의 자원에 대한 연산과 제어를 관리"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"시스템 서비스 제공(가상)"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자에게 컴퓨터의 프로그램을 쉽고 효율적으로 실행할 수 있는 환경 제공"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"가상 환경을 제공해준다 = 컴퓨터를 사용하면서 사용하는 바탕화면, 앱 아이콘 등"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"실제로는 없는 환경을 제공, 바탕화면은 사용을 위해 만든 가상 환경"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"자원 관리","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"컴퓨터 시스템 하드웨어 및 소트으웨어 자원을 여러 사용자 간에 효율적 할당, 관리, 보호","type":"text"}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"가장 눈여겨 볼 것은 자원관리"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/jcoJKAX.png","type":"image"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"가장 중요한 관리는 프로세서이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"여러개의 프로세스가 한번에 실행되기 때문에 많은 연산이 CPU에 몰릴 수 밖에 없다. 따라서 운영체제의 입장에선 이 연산을 어떻게 배분할 것인지가 포인트"}]}]},{"content":[{"inlineContent":[{"text":"프로세스의 상태","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"생성","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"프로세스가 만들어진다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"준비"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"중앙처리장치에 의해 프로세스가 실행되기를 기다린다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"실행"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"중앙처리장치에 의해 프로세스가 실행된다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"대기"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"어떤 사건이 일어나기를 기다린다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"종료"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로세스가 종료된다."}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하나의 프로세서를 여러개의 프로세스들이 점유를 해야하고 역할을 수행하도록 해야하기 때문에 이를 OS가 진행"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"프로세스 스케쥴링"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"FCFS(First-Come First-Served)","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"준비 상태 큐에 도착한 순서에 따라 차례로 CPU를 할당해준다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"비효율적인 문제가 생긴다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"SJF(Shortest Job First)","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"실행 시간이 가장 짧은 프로세스에게 먼저 CPU를 할당해준다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"평균 대기시간이 가장 적은 알고리즘이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"실행시간이 긴 프로세스에 밀려 무한 연기상태 발생가능"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"RR(Round Robin)"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"시분할 시스템을 위해 고안된 방식"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"FCFS 기법 변형"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"각 프로세스는 시간 할당량 동안만 실행된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"완료되지 않으면 다음 프로세스에게 CPU를 넘겨주고 준비상태 큐의 가장 뒤로 배치된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"할당된 시간이 클수록 FCFS와 비슷하다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"할당시간이 작을 수록 문맥교환과 오버헤드가 자주 발생한다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"Priorty Based","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/fe6O9x6.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로세스마다 우선순위를 부여한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"우선순위가 동일한 경우 FCFS 기법으로 할당한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"가장 낮은 순위를 부여받은 프로세스의 무한연기 발생 가능"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Multi Queue"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"프로세스를 특정 그룹으로 분류할 수 있을 경우 그룹에 따라 각기 다른 준비단계 큐 사용"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"준비 상태 큐 마다 다른 스케줄링 기법 사용가능","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"다른 준비상태 큐로 이동 불가"}]}]},{"content":[{"inlineContent":[{"text":"하위단계 큐에 있는 프로세스를 실행하는 도중이라도 상위 단계 준비상태 큐에 프로세스가 들어오면 상위단계 프로세스에세 CPU를 할당해준다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"운영체제마다 스케쥴링 방식이 다르고 복잡도도 위의 것보다 훨씬 크다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"주기억 장치 관리","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"단순 관리"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"가상메모리"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"보조기억장치를 주기억장치처럼 활용 = swap"}]}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/iMrFpuI.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"파일관리"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"응용프로그램들이 디스크의 파일을 읽고 쓰고 하고싶을 땐 운영체제를 통해 진행한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"왜냐하면 응용프로그램들은 어떻게 파일을 읽고 쓰는지 모르기 때문이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"파일 시스템의 종류가 굉장히 다양하다. 또한 디스크 전체의 영역을 쓰는 방식이 운영체제마다 다르다."}]}]},{"content":[{"inlineContent":[{"text":"마치 도서관의 사서처럼 책이 어디에 있고 어디에 정리해놔야하는지 알고 있는데 외부사람들은 모르기 때문에 사서에게 책을 가져다달라고 하거나 저장해달라고 하는 것 처럼","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"파일 시스템"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Unix"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Unix File System","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Linus"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"확장 파일 시스템, ZFS, XFS","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"macOS"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"HFS, HFS+, AFPS"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Windows"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"FAT"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"NTFS","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"커널","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"운영체제의 핵심이자 정체성","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"보안, 자원관리, 추상화"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"페도라, 우분투, 레드햇 등의 리눅스 계열의 운영체제들은 리눅스라는 커널을 사용하고 있는 것"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"커널로 운영체제를 구분할 수 있다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"컴퓨터의 동작","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"아래 내용이 정답은 아니다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/0TCYFPO.png"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"예를 들어 음악을 재생하고자 할 때"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"STEP1"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자가 음악 재생 버튼 클릭"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"입력장치(마우스)가 클릭신호를 수신"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"STEP2","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"입력장치(마우스)가 OS에게 사용자 입력 전달","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"OS(운영체제)는 클릭된 곳이 어디인지 판단하여 입력 전달"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"APP(응용프로그램)은 클릭 전달받아 재생명령 수신"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"STEP3"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"APP(응용프로그램)은 보조기억장치(HDD)에 음악데이터 요청"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"HDD(보조기억장치)는 음악 데이터를 주기억장치로 복사","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"RAM(주기억장치)는 음악데이터를 수신 후 OS에 수신되었음을 알림"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"STEP4"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"RAM(주기억장치)는 CPU로 연산명령 전달"}]}]},{"content":[{"inlineContent":[{"text":"CPU(중앙처리장치)는 명령어를 해독하여 연산수행 후 주기억장치로 결과 전달","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"RAM(주기억장치)는 CPU의 연산결과를 수신"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"STEP5"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"RAM(주기억장치)는 OS에 연산이 끝났음을 알림"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"OS(운영체제)는 응용프로그램으로 결과 전달"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"APP(응용프로그램)은 연산결과 수신 후 결과에 따른 명령처리"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"STEP6"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"APP(응용프로그램)은 OS에게 음악 출력 요청"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"OS(운영체제)는 출력장치에 음악 출력 요청","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"출력장치(스피커)는 전달된 데이터를 사용자에게 출력","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"멀티코어 \/ 멀티테스킹"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"한번에 여러가지 작업을 하는 것처럼 보이는 것이 멀티테스킹이다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"싱글코어 멀티테스킹","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"CPU가 하나일 때 여러가지 작업을 잘게잘게 아주 작은 단위로 나눠서 명령을 수행하는 것이기 때문에 동시에 일어나는 일처럼 보인다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"멀티코어 멀티테스킹"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"CPU가 두개면 진짜 동시에 여러가지 일을 하게 된다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[데이터베이스]","type":"text"}]}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"여러사람에 의해 공유되어 사용될 목적으로 통합하여 관리되는 데이터의 집합"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"통합된 정보들을 저장하여 운영할 수 있는 공용 데이터들의 묶음"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"자료를 디스크에 차곡차곡 쌓아놓는 방법론"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"자료구조 vs 데이터베이스"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"자료구조는 대부분 주기억장치에서 이루어질 내용","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱이 실행중인 경우, 프로그램이 실행중이여서 프로세스에 올라가있는 경우 그 프로세스 안에서 일처리들을 잘 해낼 수 있도록 데이터의 구조를 확실하게 컴팩트하게 제어하는 기술적인 방법론"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"데이터베이스는 대부분 보조기억장치에서 이루어질 내용"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터를 디스크에 반영구적으로 저장하려고 하는 것"}]}]},{"content":[{"inlineContent":[{"text":"어떤 목적으로 활용할 것인지에 따라 내용과 형태가 달라진다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"종류"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"관계형"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"relationShip database, 흔히 많이 쓰는 유형(RDB)","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Key-value"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Object","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Document"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"column"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"DBMS","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DataBase Management System"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터베이스에 접근할 수 있는 기능을 제공하는 소프트웨어"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉, 데이터베이스계의 운영체제"}]}]},{"content":[{"inlineContent":[{"text":"MySQL, PostgreSQL, SQLite, MariaDB 등","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SQL"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Structured Query Language"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"구조화된 질의어","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"하나의 언어, 데이터를 관리하기 위한 구조화된 질의문을 작성하기 위한 언어","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"관계형 데이터베이스 관리 시스템에서 사용","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"야곰의 재미난컴퓨터이야기 Part3"}]}]}]}]}]}]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211215_-----_------"},"schemaVersion":{"patch":0,"minor":3,"major":0},"sections":[],"kind":"article","metadata":{"roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"role":"article","title":"211215 운영체제와 데이터베이스.md"},"references":{"https://i.imgur.com/fe6O9x6.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/fe6O9x6.png"}],"type":"image","identifier":"https:\/\/i.imgur.com\/fe6O9x6.png","alt":null},"https://i.imgur.com/uQbcd8n.png":{"type":"image","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/uQbcd8n.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/uQbcd8n.png"},"https://i.imgur.com/jcoJKAX.png":{"variants":[{"url":"https:\/\/i.imgur.com\/jcoJKAX.png","traits":["1x","light"]}],"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/jcoJKAX.png"},"https://i.imgur.com/iMrFpuI.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/iMrFpuI.png"}],"type":"image","identifier":"https:\/\/i.imgur.com\/iMrFpuI.png","alt":null},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/0TCYFPO.png":{"variants":[{"url":"https:\/\/i.imgur.com\/0TCYFPO.png","traits":["1x","light"]}],"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/0TCYFPO.png"},"https://i.imgur.com/tTPJnj2.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/tTPJnj2.png"}],"type":"image","identifier":"https:\/\/i.imgur.com\/tTPJnj2.png","alt":null},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211216_sync,_async,_parallel,_concurrency,_gcd.json b/docs/data/documentation/arinote/211216_sync,_async,_parallel,_concurrency,_gcd.json new file mode 100644 index 0000000..43a3f4d --- /dev/null +++ b/docs/data/documentation/arinote/211216_sync,_async,_parallel,_concurrency,_gcd.json @@ -0,0 +1 @@ +{"metadata":{"modules":[{"name":"Ari's Notes"}],"role":"article","title":"211216 sync, async, Parallel, Concurrency, GCD","roleHeading":"Article"},"primaryContentSections":[{"content":[{"text":"TIL (Today I Learned)","level":1,"anchor":"TIL-Today-I-Learned","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"12월 16일 (목)"}]},{"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용","level":2},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"동시성 프로그래밍 활동학습"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"GCD란?"},{"text":" ","type":"text"},{"type":"text","text":" "}]}]}],"type":"unorderedList"},{"text":"고민한 점 \/ 해결 방법","type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[동시성 프로그래밍 활동학습]","type":"text"}]}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"동시성(Concurrency) 프로그래밍, 병렬(Parallelism) 프로그래밍은 어떻게 다르며, 우리가 주로 접할 iOS 환경에서는 무엇을 더 고려해봐야 할까?"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"동시성(Concurreny)"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"논리적인 용어","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"동시에 실행되는 것처럼 보임","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"싱글 코어에서 멀티스레드를 동작시키기 위한 방식"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"멀티 코어에서도 동시성은 사용가능"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"멀티태스킹을 위해 여러 스레드가 번갈아가면서 실행되는 성질","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"동시성을 이용한 싱글 코어의 멀티 태스킹은 각 스레드들이 병렬적으로 실행되는 것처럼 보이지만 사실은 번갈아가면서 조금씩 실행되고 있는 것"}]}]}]}]},{"content":[{"inlineContent":[{"text":"병렬성(Parallelism)","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"물리적인 용어"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"실제로 작업이 동시에 처리되는 것"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"멀티코어에서 멀티스레드를 동작시키는 방식"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"한 개 이상의 스레드를 포함하는 각 코어들이 동시에 실행되는 성질"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"병렬성은 데이터 병렬성(Data Parallelism)과 작업 병렬성(Task Parallelism)으로 구분"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터 병렬성 : 전체 데이터를 나누어 서브 데이터들로 만든 뒤, 서브 데이터들을 병렬 처리해서 작업을 빠르게 수행하는 방법이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"작업 병렬성 : 서로 다른 작업을 병렬 처리하는 것을 말한다."}]}]}]}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"동시성 프로그래밍, 병렬 프로그래밍, 동기(asynchronous)와 비동기(asynchronous)의 개념은 어떤 상관관계를 가질까?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"비동기 프로그래밍","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로그램의 주 실행 흐름을 멈추어서 기다리지 않고 다음 작업을 실행할 수 있게 하는 방식이다. 코드의 실행 및 결과 처리를 별도의 공간에 맡겨둔 뒤 그 실행결과를 기다리지 않고 다음 코드를 실행하는 병렬처리 방식이다. 비동기 프로그래밍은 언어 및 프레임워크에서 지원하는 여러 방법으로 구현할 수 있다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"동시성 프로그래밍","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"논리적인 용어로 동시에 실행되는 것처럼 보이는 방식이다. 싱글 코어에서 멀티스레드를 동작시키기 위한 방식으로 멀티 태스킹을 위해 여러 개의 스레드가 번갈아 가면서 실행되는 방식이다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Async vs Sync"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Async는 비동기다. 비동기란 내가 작업을 맡기고 실행되는 동안에 나는 또 다른 일을 할 수 있는 것을 뜻한다. 예를 들어 커피를 주문하고 기다리는 동안 아무것도 못하는게 아니라 다른일을 할 수 있는 그런 느낌이다. 반대로 Sync는 해당 작업이 끝날 때까지 기다려야 한다."}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"inlineContent":[{"text":"애플이 동시성 프로그래밍 또는 병렬 프로그래밍을 지원하기 위해 사용하는 기술엔 무엇무엇이 있을까?","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Grand Central Dispatch(GCD)"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"멀티 코어와 멀티 프로세싱 환경에서 최적화 된 프로그래밍을 할 수 있도록 애플이 개발한 기술이다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Operation Queue"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"비동기적으로 실행되어야하는 작업을 객체 지향적인 방법으로 사용한다"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Thread","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"스레드는 하나의 프로세스 내에서 실행되는 작업흐름의 단위를 말한다. 보통 한 프로세스는 하나의 스레드를 가지고 있지만 환경에 따라 둘 이상의 스레드를 동시에 실행할 수도 있다. 이러한 방식을 멀티스레딩이라고 한다. 프로그램 실행이 시작될 때부터 동작하는 스레드를 메인 스레드라고 하고 나중에 생성된 스레드를 서브 스레드 또는 세컨더리 스레드라고 한다.","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로그래밍에서 Thread는 무엇이고 iOS 환경에서 이를 다루기 위한 방법에는 무엇무엇이 있으며 그 차이는 무엇일까?"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Thread"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"스레드는 하나의 프로세스 내에서 실행되는 작업흐름의 단위를 말한다. 보통 한 프로세스는 하나의 스레드를 가지고 있지만 환경에 따라 둘 이상의 스레드를 동시에 실행할 수도 있다. 이러한 방식을 멀티스레딩이라고 한다. 프로그램 실행이 시작될 때부터 동작하는 스레드를 메인 스레드라고 하고 나중에 생성된 스레드를 서브 스레드 또는 세컨더리 스레드라고 한다.","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"iOS 환경에서의 동시성 프로그래밍 지원 종류","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"GCD (Grand Central Dispatch) : 멀티 코어와 멀티 프로세싱 환경에서 최적화된 프로그래밍을 할 수 있도록 애플이 개발한 기술이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Operation Queue : 비동기적으로 실행되어야 하는 작업을 객체 지향적인 방법으로 사용한다","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Thread : 멀티스레드 프로그래밍을 위한 애플에서 제공하는 스레드 클래스","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[동기 프로그래밍]"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"하드웨어 시스템들은 다 동기에 맞춰 동작한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"서버에 있는 소스를 가져올 때 다 받아올 때 까지 멈춰있는다."}]}]}]},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[비동기 프로그래밍]"}]}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"작업을 분리해서 사용해야한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"서버가 있을 것이고 앱은 데이터를 요청해서 화면에 표시하는 형태로 앱이 만들어질 것이다."}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Parallel(병렬) vs Concurrency(동시)]"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"병렬"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"물리적"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"물리적 스레드에서 실제 동시에 일을 하는 개념"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"동시"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"논리적"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"메인 스레드가 아닌 다른 소프트웨어적인 스레드에서 동시에 일을 하는 개념","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"text":"Concurrent Programming","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"장점"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UI 화면은 언제나 반응성을 유지해야 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"병렬 실행은 더 빠르다"}]}]}]}]},{"content":[{"inlineContent":[{"text":"비용","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"제대로 멀티스레드 코드를 작성하기에 무지막지하게 어렵다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"확장 가능하게 여러 작업을 동시에 처리하는 일반적인 문제들"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그럼에도 배워야한다 왜?"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"병렬성과 동시성이 좋아지는 시대가 왔다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"코어를 늘리는게 대세다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이제는 스레드를 만드는게 중요한게 아니다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"스레드를 생성하고 재사용하는건 운영체제가 해줄태니까 개발자들은 그 스레드에 해당하는 큐에다가 클로저를 넣어주기만 해 > 큐 단위로 클로저에서 큐에서 꺼내서 클로저들을 스레드에다가 붙여서 그 스레드에 대해서 실행하고 또 처리해줄게."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"더 작은 클로저 단위로 큐에다가 코드를 넣어주면 클로저 단위로 꺼내서 어떤 스레드에서 동작하게 만들지를 이제 클로저 단위로 작업 스케줄에서 만들자 라는게 컨셉이다."}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]}]},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[Grand Central Dispatch]","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"커널 위에 바로 만들어져 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"맥에나 iOS에서는 lib 시스템이라는 프레임워크 안에 들어있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"스레드를 잘 다루는 것이 어렵더라. 잘쓰는 방법을 만들어보자 해서 시작하게 되었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"코드블럭 단위를 클로저로 묶어서 그거를 큐에다 넣고 큐가 그거를 실행하는 이런 구조로 만들자는 의도"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"특장점","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"반응성을 개선"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"다이나믹 확장"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"보다 나은 프로세스 사용"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"깔끔한 코드"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"스레드를 많이 만드는게 좋은 일이 아니다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"적정한 갯수만큼만 cpu 구조에 맞게 운영하는게 좋을탠데, 개발자들이 직접 관리하는 것 보다는 운영체제가 cpu 아키텍처에 맞게 운영하는게 더 좋다 그걸 GCD가 해주겠다. 라는거다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"큐를 사용한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"순차적으로 실행되는 구조는 동일하다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이벤트 소싱이 가능해진다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"어떤 이벤트가 발생하면 같은 방식으로해서 인터럽트가 발생했을 때 그 처리도 같은 식의 q에서 처리되는 것처럼 사용할 수 있게 된다."}]}]}]}]},{"content":[{"inlineContent":[{"text":"디스패치 소스에 해당하는 것도 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"GCD가 관리하는 Pool이 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"더 좋은 구조에서는 Pool이 더 많을 수도 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"GCD를 사용하므로써 얻는 이점","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"효율성","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"코드가 CPU 사이클을 더 효과적으로"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"더 나은 표현","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"선언하기 편한 클로저","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"프로듀서\/컨슈머 관계를 표현하는 큐(Queues)","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"시스템 수준 관리","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"서브 시스템에 독립적으로 OS가 관리"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"기존 스레드 처리나 동기화 방식과 100% 호환 가능"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"GCD 스레드는 POSIX 스레드를 포장"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"GCD 스레드를 cancel, exit, kill, join, or detach 하지마라","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"GCD 스레드를 재사용","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"클로저 내부에서 스레드별 상태 변화를 복구하라"}]}]}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스레드 자체는 GCD가 관리하기 때문에 신경 쓸 필요가 없다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스레드 클래스 예제"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/sOiOzwg.png","type":"image"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"GCD를 사용하면 단 2줄로 줄일 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/DkYpdKM.png"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"GCD Advantages","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Thread recycling"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"스레드를 재사용하기 때문에 효율성이 좋아진다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"Deferred based on availability","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"보호하기 Locking"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Enforce mutually exclusive access to critical sections","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Serialize access to shared state between thrads"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Ensure data integrity"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/K9ssUAY.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"위 처럼 lock을 넣어줘야 멀티쓰레드 환경에서 안전한 스레드 세이프 함수가 된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"lock이 없으면 순차적으로 접근하는게 보장이 안되기 때문에 어느 순간 동시에 쓸려그러다가 앱이 멈출수도 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"동시에 쓰려고 했는데 하나를 쓰기전에 다른게 먼저 써줘가지고 값이 깨질 수도 있다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"원하는 표현이 안될 수도 있다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"GCD를 활용한다면 아래처럼 사용해주면 된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/fwVrfjp.png","type":"image"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"위 코드 블럭만큼은 한 덩어리로 처리가 된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"한 덩어리로 큐에 들어가기 때문에 충돌 날 일이 없다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"위 빨간 박스 아래 코드를 다 실행할 때 까지 동기화가 되서 멈춰져 있다. 실행이 끝나고 나면 아래로 내려간다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/8dTfvwf.png"}]}]},{"content":[{"inlineContent":[{"text":"async가 되면 큐에다가 넣는 작업을 하고 그냥 빠져나간다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"sync로 하던 async로 하던 클로저 안은 충돌나지 않고 각각의 큐에 있는 클로저가 순서대로 실행되기 때문에 순서가 보장된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 Lock을 쓰지 않더라도 GCD에서는 Lock을 쓰는 효과가 있다는 것이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/ZKhkin4.png","type":"image"}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/yMGvuwB.png"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"특정한 시간 뒤에 이 코드 블럭을 실행시켜주는 것을 보장해준다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프레임 드랍이 발생할 정도로 메인쓰레드가 바쁘면 이것도 딜레이가 되어 호출이 늦어질 수도 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"화면처리를 하는게 아니라면 꼭 메인스레드에 넣을 필요없다. 다른 쓰레드에 넣어도 된다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/T98CHyL.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"‘나는 백그라운드 어딘가에서 요 코드가 되기만 하면 돼’ 그러면 글로벌 큐에 넣어줄 수도 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"큐의 종류"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"동시성 큐"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Concurrent Queue Execute multiple blocks at the same time"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Concurrently executed closures may complete out of order","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Queues execute concurrently with respect to other queues"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"시리얼 큐","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Lightweight list of closures"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Enqueue and dequeue are FiFO"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Serial queue execute blocks one at a time","type":"text"}]}]}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"큐는 기본적으로 직렬성이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"직렬성으로 만들건지 동시성으로 만들건지 선택할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"아무 옵션을 주지않으면 직렬큐로 만들어지고 글로벌 큐로 만들면 동시성으로 만들어진다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"동시성에 있는 큐는 큐가 여러개다.","type":"text"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/6YNThOn.png","type":"image"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"DispatchGroup"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"여러작업들을 할 때 그 작업들이 한꺼번에 그룹핑해서 끝나야지만 다 끝났다라고 판단할 때 사용"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"DispatchQoS","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"우선순위를 정한다. 큐 중에서도 어떤 작업들이 먼저 들어가야될지 말아야 될지를 관리해준다."}]}]}]}]},{"content":[{"inlineContent":[{"text":"DispatchWorkItemFlags","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"옵션들을 조정할 수 있다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"concurrentPerform?"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"타입메서드"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"for문 처럼 같은 작업을 여러번 반복해야하는 것들, 클로저를 N번 실행해야할 때 사용한다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Suspending and resuming execution"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"func suspend()","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"func resume()"}]}]}]}]},{"content":[{"inlineContent":[{"text":"큐를 사용할 때 유의할 점","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"일반적인 목적의 데이터 구조가 아니다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"GCD에서 사용하기 위한 흐름 제어를 위한 것이다","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"큐에 블록을 한번 추가하면 반드시 실행된다는 것을 보장해준다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"동기화 sync API를 사용할 때는 조심해야 한다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Dead lock 상태로 멈춰버릴 수 있다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"취소하려면 DispatchWorkItem 또는 OperationQueue","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/9FE0knz.png","type":"image"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"GCD Design Patterns"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"쉬운방법"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Dispatch_async()를 써라"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"큐는 프로듀서\/컨슈머 구조로"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"클로저가 작업 사이에 데이터를 옮겨준다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"큐는 가볍고 효율적으로"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"스레드가 자동적으로 생성되거나 재사용된다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UI 이벤트 주도 개발과 비슷함"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"다른 이벤트를 기다리기 위해서 스레드를 멈추거나 폴링하지 마라","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"예) 소켓대기, 디렉토리 변화 감시","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"디스패치 소스 고려","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"운영체제 이벤트를 감시하거나 요청에 대한 응답을 기다릴 때"}],"type":"paragraph"}]}]}]}]}]}]}],"kind":"content"}],"sections":[],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211216_sync,_async,_Parallel,_Concurrency,_GCD"},"kind":"article","schemaVersion":{"minor":3,"major":0,"patch":0},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211216_sync,_async,_parallel,_concurrency,_gcd"]}],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/8dTfvwf.png":{"identifier":"https:\/\/i.imgur.com\/8dTfvwf.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/8dTfvwf.png"}],"type":"image"},"https://i.imgur.com/ZKhkin4.png":{"identifier":"https:\/\/i.imgur.com\/ZKhkin4.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/ZKhkin4.png"}],"type":"image"},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]},"https://i.imgur.com/DkYpdKM.png":{"identifier":"https:\/\/i.imgur.com\/DkYpdKM.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/DkYpdKM.png"}],"type":"image"},"https://i.imgur.com/fwVrfjp.png":{"identifier":"https:\/\/i.imgur.com\/fwVrfjp.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/fwVrfjp.png"}],"type":"image"},"https://i.imgur.com/yMGvuwB.png":{"identifier":"https:\/\/i.imgur.com\/yMGvuwB.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/yMGvuwB.png"}],"type":"image"},"https://i.imgur.com/sOiOzwg.png":{"identifier":"https:\/\/i.imgur.com\/sOiOzwg.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/sOiOzwg.png"}],"type":"image"},"https://i.imgur.com/9FE0knz.png":{"identifier":"https:\/\/i.imgur.com\/9FE0knz.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/9FE0knz.png"}],"type":"image"},"https://i.imgur.com/K9ssUAY.png":{"identifier":"https:\/\/i.imgur.com\/K9ssUAY.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/K9ssUAY.png"}],"type":"image"},"https://i.imgur.com/T98CHyL.png":{"identifier":"https:\/\/i.imgur.com\/T98CHyL.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/T98CHyL.png"}],"type":"image"},"https://i.imgur.com/6YNThOn.png":{"identifier":"https:\/\/i.imgur.com\/6YNThOn.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/6YNThOn.png"}],"type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211217_gcd.json b/docs/data/documentation/arinote/211217_gcd.json new file mode 100644 index 0000000..82322de --- /dev/null +++ b/docs/data/documentation/arinote/211217_gcd.json @@ -0,0 +1 @@ +{"kind":"article","primaryContentSections":[{"kind":"content","content":[{"level":1,"text":"TIL (Today I Learned)","type":"heading","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"type":"text","text":"12월 17일 (금)"}]},{"type":"heading","text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"GCD를 반복학습…"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앨런의 동시성 프로그래밍 강의"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"야곰닷넷 동시성 프로그래밍 강의"},{"text":" ","type":"text"},{"type":"text","text":" "}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","level":2},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[GCD 1]","type":"text"}]}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"스레드 == 일하는 녀석들"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"왜 스크롤을 내렸을 때 버벅였나?"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"여러개의 스레드가 있지만 한개의 스레드에서만 일을 시켰기 때문이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"다른 스레드에 분산시켜서 처리하지 못했기 때문이다."}]}]}]}]},{"content":[{"inlineContent":[{"text":"어떻게 다른 스레드로 작업을 분산시킬 수 있을까?","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"작업을 어떻게 다른 스레드에서 동시에 일을 하게 할 수 있을까?"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이것이 바로 동시성 프로그래밍이다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"iOS 프로그래밍에서는 작업을 대기 행렬(Queue)에 보내기만 하면 알아서 OS가 다른 스레드로 분산처리를 하고 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Queue는 항상 선입선출로 동작한다 (FIFO)","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"작업이 먼저 배치 됐다고 해서 그 작업이 먼저 끝난다는 개념은 아니다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"우리가 할일","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"작업을 Queue로 보내는 코드를 배우자","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[GCD2]"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"iOS 프로그래밍에서는 Queue를 표현하는 것에는 크게 두가지가 있다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"GCD (DispathQueue)","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Operation"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"직접적으로 스레드를 관리하지 않고 Queue만 만들어서 그 안에 작업을 넣기만 하면 된다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"시스템이 알아서 스레드에 작업을 분산하여 스레드를 관리한다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"직접 스레드를 생성하는 것은 하드웨어나 일의 부하(load)와 같은 시스템에 대한 지식없이 사용하면 오히려 앱이 느려질 수 있다."}],"type":"paragraph"}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스레드보다 더 높은 레벨\/차원에서 일을 한다고 보면 된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"쉽게 다른 스레드에서 (오래걸리는) 작업들이 “비동기적으로 동작”하도록 만들어준다. (네트워크 관련)","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"어떤 API들은 내부적으로 다른 스레드에서 비동기적으로 실행되도록 설계되어있다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"어떻게 코드로 Queue에 보낼까?","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/ZyqjbsI.png"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"DispatchQueue.global().async { }","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"큐에 보낼거야, 글로벌큐에, 비동기적으로","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"아래처럼도 가능하다."}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/CSuoGv7.png","type":"image"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"클로저에 들어가는 작업은 한 단위이다."}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/rJa10vZ.png"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"작업의 단위 == “하나의 클로저” 안에 보내는 작업 자체가 묶이는 개념이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/XQiK8YH.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"GCD와 Operation의 차이"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"GCD = 디스패치큐","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"간단한 일"}]}]},{"content":[{"inlineContent":[{"text":"함수를 사용하는 작업","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Operation = 오퍼레이션 큐"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"복잡한 일"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터와 기능을 캡슐화한 객체"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"취소, 순서지정, 일시중지(상태추적)","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"GCD를 기반으로 하고 있다."}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"프로젝트의 효율성, 사례 적합성 등을 고려하여 둘중에 하나를 선택해서 사용하면 된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Operation같은 경우는 객체이기 때문에 한번 만들어놓으면 재사용 가능성이 있다는 장점이 있다."}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[GCD 3]"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/vhjSsha.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"비동기(Async)"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"코드를 통해서 Queue로 작업을 보낸다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"작업을 보내고 나서 바로 즉시 리턴하는 개념이 비동기다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉 작업을 보내고 보냈다가 그 작업이 완료되는 것을 기다리지 않고 스레드로 돌아오는 것"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"일을 시작은 시키고 작업이 끝날 때까지 기다리지 않는다. = 메인스레드가 다른 일을 시작할 수 있다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/670zQzv.png","type":"image"}]}]},{"content":[{"inlineContent":[{"text":"동기(sync)","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"작업을 보내고 해당 작업이 다 끝날 때까지 기다려주는 것 = 메인스레드가 작업이 끝날 때 까지 기다려야한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"1번 Task가 작업이 마칠 때까지 기다릴거야 라는 개념.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"동기적으로 코드를 짜게 되면 Queue에 보내는 것이 의미가 없다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"따라서 동기적으로 보내는 코드를 짜면 실질적으로는 메인스레드에서 일을 한다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 Queue에 작업을 보낼 때 보통은 대부분 async를 사용한다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"결국에는 우리는 기존에 오래 걸리던 작업을 다른 스레드로 보내고 싶은거고 그렇게 다른 스레드로 보낸 작업을 기다리지 않고 다른 일들을 하고 싶은 것이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 async 메소드를 사용하게 된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"다만 sync가 필요한 경우도 있다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"요약","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"비동기는 작업을 다른 스레드에 시킨 후 안기다려도 다음 작업을 할 수 있고, 동기는 기다렸다가 다음 작업을 진행한다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"비동기라는 개념이 일반적으로 필요한 이유는 뭘까?"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"대부분은 서버와의 통신(네트워크 작업) 때문이다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"네트워크와 관련된 작업들은 내부적으로 비동기적으로 구현된다."}]}]},{"content":[{"inlineContent":[{"text":"네트워크 상황에 따라서 그 작업이 얼마나 걸릴지 알 수 없다.","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/cGBjDZU.png","type":"image"}]}]},{"content":[{"inlineContent":[{"text":"예시로 URLSession 같은 경우에는 사용하는 것만으로도 내부적으로 알아서 다른 스레드에 대해서 이미 일을 하고 있다.","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[GCD 4]","type":"text"}]}]},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[Serial(직렬) vs Concurrent(동시)]"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"큐가 가진 특성에 대해서 이야기 해보자.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그림으로 이해해보기","type":"text"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/Ufmi1jn.png","type":"image"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Serial Queue"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"작업을 큐에 배치시키면 한개의 스레드에만 보내는 큐다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/S8LqcTq.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Concurrent Queue","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"여러개의 스레드로 작업을 배치시키는 큐다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"몇개의 스레드로 분산할지는 시스템이 알아서 결정하고, 다만 여러개의 스레드로 분산 처리를 한다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"요약","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"직렬큐는 메인에서 분산처리 시킨 작업을 다른 한개의 스레드에서 처리하는 큐","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"동시큐는 메인에서 분산처리 시킨 작업을 다른 여러갸의 스레드에서 처리하는 큐"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"동시큐가 무조건 좋아보이는 것 같은데 직렬큐는 왜 필요할까?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"직렬큐는 순서가 중요한 작업을 할 때 사용한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"동시큐는 각자 독립적이지만 유사한 여러개의 작업을 처리할 때 사용한다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"독립적이지만 유사한 여러개의 작업?","type":"text"}],"type":"paragraph"}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/4LCoi3i.png"},{"type":"text","text":" "},{"type":"text","text":"* 예시로 테이블뷰 셀 3개가 독립적이지만 유사한 작업들이다."}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[GCD와 Operation의 차이]"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Operation은 GCD 기반으로 만들어져있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"GCD와 다르게 Operation은 취소 \/ 순서지정 \/ 일시중지 등 여러가지 기능들이 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"프로젝트 효율성, 사례 적합성 등을 고려하여 GCD나 Operation을 선택해 사용하면 될 것 같다.","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[언제 Operation이 필요할까?]"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/hGbb0Zv.png","type":"image"}]}]},{"content":[{"inlineContent":[{"text":"아주 빠르게 스크롤을 내렸을 때","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/90ykKbI.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이미 지나간 영역을 표시할 필요가 있을까?"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이미지 다운로드 하는 작업을 취소하고 싶다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이런 상황에 사용하는 것이 Operation Queue이다.","type":"text"}]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[야곰닷넷 동시성 프로그래밍]","type":"text"}]}],"type":"paragraph"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Main Thread]"}]}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"앱의 기본이 되는 스레드"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱의 생명주기와 같은 생명주기를 가지는, 앱이 실행되는 동안에는 늘 메모리에 올라와있는 기본스레드"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"즉 메인 스레드가 멈추는 것은 앱이 멈추는 것이며 메인스레드가 존재하지 않으면 앱은 동작할 수 없다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"메인 스레드는 늘 메모리에 올라온 상태로 존재한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"메인 스레드에서부터 필요한 만큼의 스레드가 파생되는 것"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이때 파생되는 스레드들은 자신이 담당하는 작업이 처리되면 메모리에서 사라지게 된다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"메인 스레드는 그림자 분신술의 실체인 셈","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"특징"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"전역적으로 사용 가능"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"global 스레드들과는 다르게 Run Loop가 자동으로 설정되고 실행된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"메인 스레드에서 동작하는 Run Loop를 Main Run Loop라고 한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UI 작업은 메인 스레드에서만 작업할 수 있다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"main Queue를 sync로 직접적으로 호출하면 deadlock 상태에 빠지게 된다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"작업이 끝나기를 기다리는 sync의 특성 때문이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Main 스레드는 sync의 코드 블록이 수행되기를 기다려야 하는데, 이 때 sync 코드 블록 역시 멈춰버리는 것이다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Main 스레드에서 실행되고 있던 코드이기 때문이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 아무것도 실행되지 못하고 main 스레드는 sync가 끝나기를, sync는 main 스레드의 block-wait이 끝나기를 기다리는 상태가 되어버린다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"이러한 현상은 main Queue이기 때문에 발생하는 현상이다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Main.sync를 호출하는 방법은 아래 예시처럼 main 스레드에서 호출하지 않으면 된다.","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"codeListing","code":["import Foundation","","DispatchQueue.global().async {"," DispatchQueue.main.sync {"," for _ in 1...5 {"," print(\"😀😀😀😀😀\")"," sleep(1)"," }"," }","}","","for _ in 1...5 {"," print(\"🥶🥶🥶🥶🥶\")"," sleep(2)","}"],"syntax":"swift"},{"inlineContent":[{"inlineContent":[{"text":"[DispatchWorkItem]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"코드 블럭을 캡슐화할 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"타입을 명시하는 동시에 더 직관적인 코드를 작성할 수 있다.","type":"text"}]}]}]},{"syntax":"swift","type":"codeListing","code":["import Foundation","","let red = DispatchWorkItem {"," for _ in 1...5 {"," print(\"🥵🥵🥵🥵🥵\")"," sleep(1)"," }","}","","let yellow = DispatchWorkItem {"," for _ in 1...5 {"," print(\"😀😀😀😀😀\")"," sleep(1)"," }","}","","let blue = DispatchWorkItem {"," for _ in 1...5 {"," print(\"🥶🥶🥶🥶🥶\")"," sleep(2)"," }","}","","DispatchQueue.main.async(execute: yellow)","DispatchQueue.global().sync(excute: blue)"]},{"inlineContent":[{"inlineContent":[{"text":"[asyncAfret]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"async 메소드를 원하는 시간에 호출해줄 수 있는 메소드다."},{"type":"text","text":" "},{"type":"text","text":"DispatchQueue.global().asyncAfter(deadline: .now() + 5, execute: yellow)"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"위 코드는 지금으로부터 5초후에 yellow라는 DispatchWorkItem을 실행시킨다는 코드다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Execute 파라미터 대신 직접 코드 블럭을 구현해도 된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"deadline 대신에 walldeadline이라는 파라미터를 사용해줄 수도 있는데, qallDeadline은 시스템(기기)의 시간을 기준으로 카운트 하는 것이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉 deadline은 스톱워치로 측정하듯이 5초를 카운트해서 작업이 시작되고 wallDeadline은 지금 5시니까 5시 5초에 작업을 시작해야지와 같이 작업을 수행하는 것"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[asyncAndWait]"}]}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"비동기 작업이 끝나는 시점을 기다릴 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"aside","style":"note","name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"1️⃣ sync와 async의 차이는 무엇인가요?"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"기다리는 것, 기다리지 않는 것"}],"type":"paragraph"}]}]},{"style":"note","name":"Note","content":[{"inlineContent":[{"text":"2️⃣ async와 concurrent는 구분되는 개념이라고 했습니다. 각각을 설명해보세요.","type":"text"}],"type":"paragraph"}],"type":"aside"},{"items":[{"content":[{"inlineContent":[{"text":"Async","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"작업을 다른 스레드에 시킨 후 안기다려도 다음 작업을 할 수 있다"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Concurrent Queue"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"여러개의 스레드로 작업을 배치시키는 큐"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"몇개의 스레드로 분산할지는 시스템이 알아서 결정하고 다만 여러개의 스레드로 분산 처리를 한다."}]}]}]}]}],"type":"unorderedList"},{"content":[{"inlineContent":[{"text":"3️⃣ DispatchQueue에서 serial 큐와 main 큐는 같은 것인가요?","type":"text"}],"type":"paragraph"}],"style":"note","name":"Note","type":"aside"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"main이 기본적으로 serial 큐인 것인 부분 말고는 다르다."}]}]}],"type":"unorderedList"},{"type":"aside","style":"note","name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"4️⃣ DispatchQueue에서 main과 global()의 차이는 무엇인가요?","type":"text"}]}]},{"items":[{"content":[{"inlineContent":[{"text":"main은 앱의 기본이 되는 스레드이다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"앱이 실행되는 동안에 늘 메모리에 올라와 있는 기본 스레드"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"global은 Concurrent Queue라서 여러개의 스레드가 존재한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 화면처리를 하는게 아니라면 꼭 main에다 넣을 필요 없다. global이나 다른 스레드에 넣어줘도 된다."}]}]}],"type":"unorderedList"},{"name":"Note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"5️⃣ main 스레드는 어떤 특징을 가지고 있나요?"}]}],"style":"note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"전역적으로 사용이 가능하다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"global 스레드들과는 다르게 Run Loop가 자동으로 설정되고 실행된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"UI 작업은 메인 스레드에서만 작업할 수 있다.","type":"text"}],"type":"paragraph"}]}]},{"name":"Note","content":[{"inlineContent":[{"type":"text","text":"6️⃣ Serial에서 async는 어떻게 동작하나요?"}],"type":"paragraph"}],"type":"aside","style":"note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"쌓인 순서대로 작업이 처리된다.","type":"text"}]}]}]},{"name":"Note","content":[{"inlineContent":[{"type":"text","text":"7️⃣ serial 큐에서 sync로 작업을 처리하면 어떻게 될까요?"}],"type":"paragraph"}],"style":"note","type":"aside"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"각각의 작업이 끝나기를 기다리고 다음 작업을 처리한다."}]}]}],"type":"unorderedList"},{"style":"note","type":"aside","name":"Note","content":[{"inlineContent":[{"text":"8️⃣ main.sync를 사용하면 어떻게 되나요? 그 이유는 무엇인가요?","type":"text"}],"type":"paragraph"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"deadlock 상태에 빠지게 된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이유는 작업이 끝나기를 기다리는 sync의 특성 때문이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"sync는 코드 블럭이 처리되기 전까지 다음 코드로 넘어가지 않는 것을 확인했는데 이러한 상황을 Block-wait이라고 한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Main.sync를 호출하게 되면 main 스레드는 sync의 코드 블럭이 수행되기를 기다려야 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"sync는 main 스레드의 Block-wait이 끝나기를 기다리는 상태가 된다."}]}]}]},{"name":"Note","type":"aside","style":"note","content":[{"type":"paragraph","inlineContent":[{"text":"9️⃣ 병렬 프로그래밍과 동시성 프로그래밍은 각각 무엇인가요? 서로 반대되는 개념일까요?","type":"text"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"동시성","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"논리적인 개념","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"메인 스레드가 아닌 다른 소프트웨어적인 스레드에서 동시에 일을하는 개념"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"병렬"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"물리적인 개념"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"물리적 스레드에서 실제 동시에 일을 하는 개념"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[DispatchQueue의 초기화]"}]}]},{"type":"codeListing","code":["convenience init(label: String,"," qos: DispatchQoS = .unspecified,"," attributes: DispatchQueue.Attributes = [],"," autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = .inherit,"," target: DispatchQueue? = nil)"],"syntax":"swift"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Label","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DispatchQueue의 label을 설정해주는 파라미터다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"디버깅 환경에서 추적하기 위해 작성하는 String 값이다. 식별자와 같다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"qos","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DispatchQoS 타입의 값을 받는 파라미터"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"QoS란?"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Quality of Service의 약자로 실행될 Task들의 우선순위를 정해주는 값이다."}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Attributes","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"DispatchQueue의 속성을 정해주는 값"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":".concurrent로 초기화 한다면 다중 스레드 환경에서 코드를 처리하는 DispatchQueue가 된다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"기본값으로 아무 설정도 하지 않는 다면 Serial DispatchQueue가 만들어진다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":".initiallyInactive 속성은 작업을 큐에 담아 놓기만 하고 active()를 호출하기 전까지는 작업을 처리하지 않는 속성이다.","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"syntax":"swift","type":"codeListing","code":["import Foundation","","let yellow = DispatchWorkItem {"," for _ in 1...5 {"," print(\"😀😀😀😀😀\")"," sleep(1)"," }","}","","let myDispatch = DispatchQueue(label: \"Odong\", attributes: .initiallyInactive)","","myDispatch.async(execute: yellow) \/\/ 코드 블록 호출 안됨.","myDispatch.activate()"]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"autoreleaseFrequency","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DispatchQueue가 자동으로 객체를 해제하는 빈도의 값을 결정하는 파라미터"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"즉 객체를 autorelease 해주는 빈도이며 기본값은 inherit이다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"inherit: target과 같은 빈도를 가진다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"workItem: workItem이 실행될 때 마다 객체들을 해제한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Never: autorelease를 하지 않는다.","type":"text"}]}]}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Target"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"코드 블럭을 실행할 큐를 target으로 설정할 수 있다."}]}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[QoS]","type":"text"}],"type":"strong"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"여기서 말하는 우선 순위는 무엇에 더 많은 에너지를 쏟을까? 와 같은 맥락이다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"더 많은 에너지를 쏟는다는 것은 더 많은 스레드를 할당한다는 이야기다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"우선순위 종류"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"User-interactive"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Main 스레드에서 작업하며 사용자 인터페이스, 새로고침, 애니메이션 등 사용자와 상호 작용하는 작업에 할당한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"작업이 빠르게 수행되지 않으면 유저 인터페이스는 멈추게 도니다. 반응성(responsiveness)과 성능(performance)에 중점을 둔다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"text":"User-initiated","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"문서를 열거나 버튼을 클릭해 액션을 수행하는 것처럼 빠른 결과를 요구하는 유저와의 상호작용 작업에 할당한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"몇 초 이내의 짧은 시간 내에 수행해야하는 작업으로 반응성과 성능에 중점을 둔다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Default"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"QoS를 할당해주지 않을 경우 기본값으로 사용되며 User initiate와 Utility의 중간 수준의 레벨"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Utility"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"데이터를 읽거나 다운로드하는 작업처럼 작업이 완료되는 데에 어느정도 시간이 걸리거나 즉각적인 결과가 요구되지 않는 작업에 할당한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"반응성, 성능, 에너지 효율의 밸런스에 중점을 둔다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Background","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"index 생성, 동기화, 백업 등 사용자가 볼 수 없는 백그라운드의 작업에 할당한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"에너지 효율에 중점을 둔다","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Unspecified"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"QoS의 정보가 없음을 나타내며 시스템이 QoS를 추론해야 한다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[Async의 파라미터에 대해서]","type":"text"}]}]},{"syntax":"swift","code":["func async(group: DispatchGroup? = nil, qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], execute work: @escaping () -> Void)"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Group","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"DispatchQueue의 async 코드 블럭을 묶어서 관리해주는 DispatchGroup이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"여러 스레드에서 비동기로 작업을 처리하다보면 여러개의 작업을 함께 관리해주어야할 때가 있다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"QoS"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앞서 설명했던 DispatchQueue와 같은 내용이다."}]}]},{"content":[{"inlineContent":[{"text":"역시 적절한 케이스를 설정해주면 시스템이 알아서 관리해준다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"Flags","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"DispatchWorkItemFlags 타입의 값을 받는 파라미터","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"코드 블럭을 실행할 때 추가 속성을 결정한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기본값은 아무 속성도 부여하지 않는다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"여러가지 속성을 한번에 부여할 수도 있다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"assingCureentContext"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"코드 블럭을 실행하는 Context(queue 혹은 스레드)의 속성을 상속받았다. QoS와 같은 속성을 동일하게 한다는 이야기다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"barrier","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Concurrent queue 환경에서 barrier(장벽, 차단) 역할을 한다. barrier 속성의 코드 블럭이 실행되기 전에 실행되었던 코드들은 완료까지 실행되고 barrier 속성의 코드블럭이 실행되기 전까지 다른 코드 블럭은 실행되지 않는다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"detached"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"실행할 코드 블럭에 실행중인 context(queue 혹은 스레드)의 속성을 적용하지 않는다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"enforceQoS","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"실행중인 context의 QoS보다 실행할 코드 블럭의 QoS에 더 높은 우선 순위를 부여한다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"inheritQoS"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"enforceQoS와 반대로 실행중인 context의 QoS에 더 높은 우선 순위를 부여한다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"noQoS"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"QoS를 할당하지 않고 코드 블럭을 실행시킨다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"assingCureentContext보다 우선시 되는 속성이다.","type":"text"}],"type":"paragraph"}]}]}]}]}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[completionHandler]","type":"text"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이미 구현되어있는 코드들 중에 completionHandler이나 completion 이라는 클로저를 가진 메소드들은 함수의 실행 순서를 보장받을 수 있는 클로저다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"특히 escaping 클로저는 함수의 실행이 끝나면 함수의 밖에서 실행되는 작업들이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"completionHandler와 같은 클로저를 사용하는 경우에는 비동기 메서드일 때에도 작업이 종료되는 시점을 추적할 수 있고 순서를 보장받을 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"대표적인 예로 URLSession이 있다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"서버와의 통신을 도와주는 API"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"서버에서 데이터를 받아오는 메소드를 비동기로 실행시킨다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하지만 비동기로 작업이 처리되는 경우 그 작업이 언제 끝날지를 정확하게 파악할 수 없다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"스레드는 시스템이 관리해주기 때문이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"때문에 completionHandler 혹은 completion와 같은 클로저를 구현해준다면 작업이 끝나는 시점에 원하는 동작을 수행시켜줄 수 있는 것이다.","type":"text"}]}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[DispatchGroup]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"비동기적으로 처리되는 작업들을 그룹으로 묶어 그룹 단위로 작업 상태를 추적할 수 있는 기능이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉 사용하면 async들을 묶어서 그룹의 작업이 끝나는 시점을 추적하여 어떠한 동작을 수행시킬 수가 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이 때 묶어줄 async 작업들이 꼭 같은 큐, 스레드에 있지 않더라도 묶어줄 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"DispatchGroup은 async에서만 사용할 수 있다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"비동기로 처리되는 작업은 작업이 끝나는 시점을 정확하게 예측하기 어렵기 때문이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"동기의 경우 작업 종료 시점을 따로 추적할 필요가 없다. 작업이 처리되기를 반드시 기다렸다가 다음 작업을 수행하기 때문이다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용하는 방법"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"aysnc를 호출하면서 파라미터로 group을 지정해준다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Enter, leave를 코드의 앞뒤로 호출하여 group을 지정해준다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"enter와 leave는 DispatchGroup이 enter()부터 leave()까지 포함된다라는 의미"}]}]}],"type":"unorderedList"}]}]}]}]},{"type":"codeListing","code":["let group = DispatchGroup()","","\/\/ enter, leave를 사용하지 않는 경우","DispatchQueue.main.async(group: group) {}","DispatchQueue.global().async(group: group) {}","","\/\/ enter, leave를 사용하는 경우","group.enter()","DispatchQueue.main.async {}","DispatchQueue.global().async {}","group.leave()"],"syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이렇게 묶어낸 그룹에 대해 notify() 혹은 wait()으로 작업을 추적해줄 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"notify"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"DispatchGroup의 업무가 끝나는 시점에 원하는 동작을 수행하기 위한 메소드다.","type":"text"}]}]}]}]}]},{"code":["import Foundation","","let red = DispatchWorkItem {"," for _ in 1...5 {"," print(\"🥵🥵🥵🥵🥵\")"," sleep(1)"," }","}","","let yellow = DispatchWorkItem {"," for _ in 1...5 {"," print(\"😀😀😀😀😀\")"," sleep(1)"," }","}","","let blue = DispatchWorkItem {"," for _ in 1...5 {"," print(\"🥶🥶🥶🥶🥶\")"," sleep(2)"," }","}","","let group = DispatchGroup()","","DispatchQueue.global().async(group: group, execute: blue)","DispatchQueue.global().async(group: group, execute: red)","","\/\/ group.enter()","\/\/ DispatchQueue.global().async(execute: blue)","\/\/ DispatchQueue.global().async(execute: red)","\/\/ group.leave()","","group.notify(queue: .main) {"," print(\"모든 작업이 끝났습니다.\")","}"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"위 코드가 실행되면 notify 메서드에 의해 group의 모든 작업이 끝나기를 기다렸다가 코드블럭을 실행시켜준다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이때 notify의 파라미터 queue는 코드블럭을 실행시킬 queue를 말한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Wait","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DispatchGroup의 수행이 끝나기를 기다리기만 하는 메서드다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"notify와 달리 별도의 코드블럭을 실행하지 않는다."}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"code":["let group = DispatchGroup()","","DispatchQueue.global().async(group: group, execute: blue)","DispatchQueue.global().async(group: group, execute: red)","","group.wait()","print(\"모든 작업이 끝났습니다.\")","","\/\/ group.wait(timeout: 10)","\/\/ print(\"모든 작업이 끝났습니다.\")"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Wait 메서드에는 timeout 파라미터를 설정해줄 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"만약 10을 전달하면 group을 딱 10초 동안만 기다리는 것이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"만약 10초가 넘어갔는데도 group의 작업이 끝나지 않는다면 더이상 기다리지 않고 다음 코드를 실행한다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[Race Condition]"}],"type":"strong"}]},{"type":"codeListing","syntax":"swift","code":["import Foundation","","var cards = [1, 2, 3, 4, 5, 6, 7, 8, 9]","","DispatchQueue.global().async {"," for _ in 1...3 {"," let card = cards.removeFirst()"," print(\"야곰: \\(card) 카드를 뽑았습니다!\")"," }","}","","DispatchQueue.global().async {"," for _ in 1...3 {"," let card = cards.removeFirst()"," print(\"노루: \\(card) 카드를 뽑았습니다!\")"," }","}","","DispatchQueue.global().async {"," for _ in 1...3 {"," let card = cards.removeFirst()"," print(\"오동나무: \\(card) 카드를 뽑았습니다!\")"," }","}","","\/* 출력","야곰: 1 카드를 뽑았습니다!","노루: 1 카드를 뽑았습니다!","오동나무: 1 카드를 뽑았습니다!","야곰: 2 카드를 뽑았습니다!","노루: 5 카드를 뽑았습니다!","야곰: 6 카드를 뽑았습니다!","노루: 8 카드를 뽑았습니다!","오동나무: 7 카드를 뽑았습니다!","오동나무: 9 카드를 뽑았습니다!","*\/"]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"코드가 동작은 하지만 제대로 동작하고 있지는 않다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"이런 문제가 발생한 이유는 하나의 배열에 여러 스레드가 동시에 접근해서다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스레드가 여러개인 상황에서는 코드가 동시에 실행되어 하나의 값에 동시에 접근하는 경우가 발생할 수 있는데 이러한 경우는 Race Condition이라고 한다."}]}]}],"type":"unorderedList"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[Thread Safe]"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Race Condition이 발생하는 이유는 Swift의 배열이 Thread Safe하지 않기 때문이다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Thread Safe하다는 것은 여러 스레드에서 동시에 접근이 불가능한 것을 말한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이러한 문제들을 해결해보려면 어떻게 해야할까?"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"DispatchSemaphore를 활용하여 접근할 수 있는 스레드의 수를 제어해주거나","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Serial Queue를 활용한다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Race Condition이 발생하는 이유는 여러 스레드에서 질서없이 배열에 접근했기 때문이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그러므로 Serial Queue로 질서를 만들어 주면 문제를 해결할 수 있게 된다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["import Foundation","","var cards = [1, 2, 3, 4, 5, 6, 7, 8, 9]","let pickCardsSerialQueue = DispatchQueue(label: \"PickCardsQueue\")","","DispatchQueue.global().async {"," for _ in 1...3 {"," pickCardsSerialQueue.sync {"," let card = cards.removeFirst()"," print(\"야곰: \\(card) 카드를 뽑았습니다!\")"," }"," }","}","","DispatchQueue.global().async {"," for _ in 1...3 {"," pickCardsSerialQueue.sync {"," let card = cards.removeFirst()"," print(\"노루: \\(card) 카드를 뽑았습니다!\")"," }"," }","}","","DispatchQueue.global().async {"," for _ in 1...3 {"," pickCardsSerialQueue.sync {"," let card = cards.removeFirst()"," print(\"오동나무: \\(card) 카드를 뽑았습니다!\")"," }"," }","}"]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[DispatchSemaphore]"}]}]},{"code":["class DispatchSemaphore : DispatchObject"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"공유 자원에 접근할 수 있는 스레드의 수를 제어해주는 역할을 한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"몇개의 스레드에 접근을 허용할 것인지 제어할 수 있기 때문에 접근을 1개의 스레드만 허용한다면 Race Condition을 방지할 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DispatchSemaphore는 semaphore count를 카운트하는 식으로 동작한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"예시로 하나의 스레드가 접근을 하면 count에 -1을, 접근이 끝나면 count에 +1을 해준다."}]}]}],"type":"unorderedList"},{"syntax":"swift","code":["let semaphore = DispatchSemaphore(value: 1) \/\/ count = 1","","DispatchQueue.global().async {"," semaphore.wait() \/\/ count -= 1",""," semaphore.signal() \/\/ count += 1","}"],"type":"codeListing"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"wait()은 값에 접근했다고 알리는 메소드, signal()은 볼 일 다봤다는 메소드"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"주의사항은 반드시 두 메소드를 짝지어서 호출해주어야 한다."}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[UI 작업이 왜 Main Thread에서만 가능할까?]"}],"type":"strong"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIKit은 Thread Safe하지 않다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIKit의 UI요소들이 여러 스레드에서 접근가능하게 되면 어떤일이 발생할까?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"앞서 배웠던 것처럼 Racd Condition이 발생하게 된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"버튼의 모양을 바꾸는 일, 버튼의 위치를 이동시키는 일, 버튼의 색을 바꾸는 일 등이 충돌할 수 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"그러므로 UIKit 작업dms Serial Queue인 Main Thread로 가져와서 작업해야한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Thread Safe하게 만들지 않은 이유는 사실 성능 측면에서 Thread Safe한 것이 효율적이지 않기 때문이다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"다른 직렬큐에 넣으면 안될까?"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"UI 작업을 꼭 메인 스레드에서 작업해야하는 이유는 메인 스레드에는 Main RunLoop가 동작하고 있기 때문이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"메인 스레드에서는 RunLoop가 일정한 주기를 유지하며 계속 동작하고 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이 주기에 맞추어서 사용자의 입력을 받아서 UI를 그리게 된다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이러한 주기를 View Drawing Cycle이라고 한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 모든 스레드의 RunLoop에 따라 각자가 UI를 그리게 된다면 UI가 그려지는 시점이 모두 제각각이 되기 때문이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그렇게 되면 비효율적일 뿐더러 Race Condition이 발생하게 된다.","type":"text"}]}]}]}]}],"type":"unorderedList"},{"type":"heading","anchor":"%EB%8A%90%EB%82%80-%EC%A0%90","level":2,"text":"느낀 점"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"어제부터 오늘까지 GCD 관련 강의를 3가지를 보았는데, 점점 이해가 채워지는 기분이 든다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앨런 강의 살까…."}]}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"https:\/\/www.youtube.com\/watch?v=EL5n2U8XNpQ&list=PLqK3bFbiW77MBn0ofPRjGDBU9Ytnz9-6S","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/yagom.net\/courses\/%eb%8f%99%ec%8b%9c%ec%84%b1-%ed%94%84%eb%a1%9c%ea%b7%b8%eb%9e%98%eb%b0%8d-concurrency-programming\/"}],"type":"paragraph"}]}]}]}]}]}],"schemaVersion":{"major":0,"minor":3,"patch":0},"sections":[],"variants":[{"paths":["\/documentation\/arinote\/211217_gcd"],"traits":[{"interfaceLanguage":"swift"}]}],"metadata":{"role":"article","roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"title":"211217 GCD"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211217_GCD","interfaceLanguage":"swift"},"references":{"https://i.imgur.com/cGBjDZU.png":{"identifier":"https:\/\/i.imgur.com\/cGBjDZU.png","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/cGBjDZU.png"}]},"https://i.imgur.com/4LCoi3i.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/4LCoi3i.png"}],"identifier":"https:\/\/i.imgur.com\/4LCoi3i.png","alt":null,"type":"image"},"https://i.imgur.com/Ufmi1jn.png":{"identifier":"https:\/\/i.imgur.com\/Ufmi1jn.png","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/Ufmi1jn.png"}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/670zQzv.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/670zQzv.png"}],"identifier":"https:\/\/i.imgur.com\/670zQzv.png","alt":null,"type":"image"},"https://i.imgur.com/90ykKbI.png":{"identifier":"https:\/\/i.imgur.com\/90ykKbI.png","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/90ykKbI.png"}]},"https://i.imgur.com/rJa10vZ.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/rJa10vZ.png"}],"identifier":"https:\/\/i.imgur.com\/rJa10vZ.png","alt":null,"type":"image"},"https://i.imgur.com/XQiK8YH.png":{"identifier":"https:\/\/i.imgur.com\/XQiK8YH.png","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/XQiK8YH.png"}]},"https://i.imgur.com/hGbb0Zv.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/hGbb0Zv.png"}],"identifier":"https:\/\/i.imgur.com\/hGbb0Zv.png","alt":null,"type":"image"},"https://i.imgur.com/vhjSsha.png":{"identifier":"https:\/\/i.imgur.com\/vhjSsha.png","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/vhjSsha.png"}]},"https://i.imgur.com/S8LqcTq.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/S8LqcTq.png"}],"identifier":"https:\/\/i.imgur.com\/S8LqcTq.png","alt":null,"type":"image"},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]},"https://i.imgur.com/ZyqjbsI.png":{"identifier":"https:\/\/i.imgur.com\/ZyqjbsI.png","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/ZyqjbsI.png"}]},"https://i.imgur.com/CSuoGv7.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/CSuoGv7.png"}],"identifier":"https:\/\/i.imgur.com\/CSuoGv7.png","alt":null,"type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211220_---_----,_----.json b/docs/data/documentation/arinote/211220_---_----,_----.json new file mode 100644 index 0000000..f32a517 --- /dev/null +++ b/docs/data/documentation/arinote/211220_---_----,_----.json @@ -0,0 +1 @@ +{"schemaVersion":{"minor":3,"patch":0,"major":0},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211220_---_----,_----","interfaceLanguage":"swift"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Xcode"]]},"kind":"article","variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211220_---_----,_----"]}],"primaryContentSections":[{"kind":"content","content":[{"type":"heading","level":1,"text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"type":"text","text":"12월 20일 (월)"}]},{"type":"heading","text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"의존성 관리도구 활동학습","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"은행 창구 매니저 STEP 1 진행"}]}]}]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","level":2},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[카훗 오답노트]"}]}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"접근성이란?"}]},{"items":[{"content":[{"inlineContent":[{"text":"사용자의 신체적 특성이나, 지역, 나이, 지식 수준, 기술 체험과 같은 제한 사항을 고려하여 가능한 많은 사용자가 불편 없이 이용할 수 있도록 제품, 서비스를 만들어 제공하는 개념","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"VoiceOver를 통해 읽어주는 Accessibility의 property"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"label"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"title"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"value","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"type"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Race Condition"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"두 개 이상의 프로세스나 스레드가 하나의 데이터를 공유할 때 데이터가 동기화되지 않는 상황을 뜻한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[라이브러리와 프레임워크 구분을 쉽게 하는 방법]"}]}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이게 없어도 앱을 만들 수 있는지를 생각하면 된다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"만들 수 있으면 라이브러리"}]}]},{"content":[{"inlineContent":[{"text":"만들 수 없으면 프레임워크","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그 위에서 코드를 작성한다면 프레임워크, 코드를 가져다 쓴다면 라이브러리"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[Dynamic과 Static]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"빌드할 때 Dynamic으로 혹은 Static으로 프레임워크를 생성할지 설정해줄 수 있다. 이 두개의 차이는 무엇일까?","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"두개의 차이는 "},{"type":"codeVoice","code":"컴파일된 코드를 참조하는 방식"},{"text":"에 있다","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Dynamic 라이브러리"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"런타임 상에서 Mach-O 파일과 연결 (","type":"text"},{"type":"codeVoice","code":"참조"},{"type":"text","text":"관계)"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"필요에 의해 동적으로 메모리에 로드 된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"라이브러리에 대한 참조만 포함","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Static 라이브러리","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱이 사용하는 프레임워크의 코드는 컴파일 시간동안 ‘Static Linker’에 의해 앱의 실행파일 안에 "},{"type":"codeVoice","code":"복사"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정적으로 메모리에 로드 된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"라이브러리 코드가 모두 포함"}]}]}]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"text":"[의존성 관리도구와 Git을 함께 사용할 때 주의할 점]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이전 버전의 프로젝트에서 외부 라이브러리의 ‘최신화’를 유지한 경우 (pod file에서 버전을 명시해주지 않으면 자동으로 최신버전 유지가 된다.)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이전 버전의 프로젝트를 사용하려는 경우에 코드는 이전 라이브러리에 맞춰져있지만 라이브러리는 최신 버전이어서 하위 호환이 안될 수 있다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"프로젝트 main 브랜치에서 라이브러리를 고정시켜놓은 경우","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"develop 브랜치에서 라이브러리를 업데이트 시키는 경우 나중에 배포를 위해 main 브랜치로 병합할 때, 라이브러리의 버전이 달라 충돌이 일어날 수 있다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"두개의 라이브러리를 채택한다고 할 때 각 라이브러리가 같은 하위 라이브러리를 채택하지만 두 라이브러리가 버전이 다른 경우 충돌이 날 수 있다."}]}]},{"content":[{"anchor":"%EB%94%B0%EB%9D%BC%EC%84%9C-%EC%A0%95%EB%A6%AC%ED%95%98%EC%9E%90%EB%A9%B4","type":"heading","text":"따라서 정리하자면…","level":3},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"외부 라이브러리의 코드를 commit에 남길 필요가 없으므로 podfile은 gitignore파일에 넣어주는 것이 좋다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"만약 gitignore에 넣지 않으면 라이브러리 코드가 업데이트될 때마다 새로운 diff(difference)가 생긴다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"코드가 github에 열려있는 한 외부 라이브러리의 코드는 언제든 가져올 수 있기 때문에 굳이 프로젝트 파일에 넣어주지 않아도 된다.","type":"text"}]}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[라이브러리를 가져올 때 특정 버전을 명시해주었을 때?]"}]}]},{"items":[{"content":[{"inlineContent":[{"text":"장점","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"명시를 하지 않을 때와 달리 어떤 버전을 가지고 사용해야하는지 버전이 명확해지는 장점이 있다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"단점"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"반대로 라이브러리가 업데이트 되면 특정한 기능을 사용할 수 없는 경우가 발생할 수도 있고, 혹은 업데이트 된 기능을 바로 사용할 수 없고 한번 재정비 후에 사용해야하는 경우도 발생할 수 있을 것 같다."}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[야곰닷넷 오픈소스 라이브러리 만들기]"}]}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"오픈소스란?","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"소스코드를 공개해 특별한 제한 없이 그 코드를 볼 수 있도록 한 소스코드"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"네트워킹 라이브러리인 Alamofire"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이미지를 관리해주는 Kingfisher"}]}]}]}]},{"content":[{"inlineContent":[{"text":"라이선스 종류에 따라 다르지만 자유롭게 사용할 수 있어 라이브러리 개선에도 참여할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"IT 생태계의 근간을 이뤄나가는 원동력에 오픈 소스가 큰 기여를 하고 있다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"오픈소스 제작의 의미"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"자신의 코드를 공개하는 것은 부끄러울 수도 있지만 그만큼 더 신경쓰고 더 고민해서 코드를 짜게 하는 원동력이 될 수도 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"왜 그렇게 짜여져 있었는지 어떻게 내가 원하는 버전을 사용할 수 있었는지 깨닫는데 도움이 될 것이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"경력 쌓기 힘든 주니어 개발자에게는 색다른 경력을 쌓는 길이 될 수도 있다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"성장한 과정을 직접적으로 보여줄 수 있기 때문에 중고신입으로 가기 위한 지름길이 아닐까?","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"text":"지나가던 고수님께서 내가 모르던 부분을 집어주실 수 있고 코드를 수정해주실 수도 있다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"의존성 관리도구"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"애플리케이션 기능을 개발하기 위해 외부 라이브러리를 사용할 때 프로젝트와 해당 라이브러리의 상관관계를 용이하게 관리해주는 도구"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"내부 모듈을 분리하여 관리할 때도 사용 가능"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Java는 Maven","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Javascript는 Yarn, NPM","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Python은 Pyenv","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"코코아, 코코아터치 애플리케이션 개발환경에는?","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"코코아팟(Cocoapods)","type":"codeVoice"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"장점"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"사용하기 쉽다"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Dynamic, Static 라이브러리를 모두 지원한다. (1.5.0 버전 이상)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"의존성의 의존성까지 자동으로 관리해준다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"누구나 쉽게 어떤 의존성이 애플리케이션에 있는지 알 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Pod outdated 명령어로 쉽게 새로운 버전이 있는지 체크할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"거의 대부분의 라이브러리가 코코아팟을 지원한다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"단점"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"라이브러리를 다운받아 설치하는데 오랜 시간이 걸린다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"프로젝트를 빌드 할 때마다 모든 팟 라이브러리가 같이 빌드되므로 다른 도구를 사용할 때 보다 프로젝트 빌드 시간이 느리다.","type":"text"}]}]}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"카르타고(Carthage)","type":"codeVoice"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"장점","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Dynamic, Static 라이브러리를 모두 지원한다. (0.30.0 버전 이상)"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"의존성의 의존성까지 자동으로 관리해준다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"누구나 쉽게 어떤 의존성이 애플리케이션에 있는지 알 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"carthage outdated 명령어로 쉽게 새로운 버전이 있는지 체크할 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"carthage update를 실행할때만 한 번 프레임워크를 빌드하므로 코코아팟에 비해 프로젝트 빌드 속도가 빠르다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"처음에 프레임워크를 추가하는 것 외에 프로젝트 설정이 바뀌지 않는다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"단점"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"의존성이 추가될 때마다 해줘야하는 번거로운 작업이 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"사용자나 인기가 많은 라이브러리가 아니라면 카르타고를 지원하지 않을 수도 있다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"스위프트 패키지 매니저(Swift Package Manager)","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"장점"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"애플이 지원한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Dynamic, Static 라이브러리를 모두 지원한다. (4.0 버전 이상)"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"의존성의 의존성까지 자동으로 관리해준다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"누구나 쉽게 어떤 의존성이 애플리케이션에 있는지 알 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스위프트 언어에 built-in 되어있어 별다른 설치가 필요없다. (Swift 3 이상)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스위프트 언어에 built-in 되었기 때문에 Xcode Project 파일이 꼭 필요한 것이 아니므로 리눅스에서도 사용할 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Pacage.swift 파일 이외에 수행할 설정이 없다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Xcode의 GUI 환경에서 관리가 가능하다 (11.0 버전 이상)"}]}]}]}]},{"content":[{"inlineContent":[{"text":"단점","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"아직은 지원하지 않는 라이브러리가 많다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"현 시점 해결되지 않은 이슈가 270건이 있다."}]}]}]}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"의존성 관리도구를 사용하는 것이 필수는 아니다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하지만 사용하지 않을 경우 그 만큼 불필요한 공수가 발생할 수 있다."}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"예를 들어 외부 라이브러리를 직접 프로젝트에 적절히 포팅하여 삽입하는 작업 같은 것"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"해당 라이브러리에 대한 업데이트가 생겨 버전이 올라간다면 라이브러리를 교체해줘야 하는 경우","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"일일히 기존 라이브러리를 삭제하고 새로 삽입시켜야 하는 경우"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이때 발생하는 휴먼 에러 또한 무시할 수 없다."}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"inlineContent":[{"code":"오픈소스 작성 시 주의할 점","type":"codeVoice"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참조: 오픈소스 라이선스 비교표"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이 중 제약조건이 낮고 자유도가 높은 MIT 라이선스에 대해서","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"중요한 내용 요약"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이 소프트웨어를 누구라도 무상으로 제한없이 취급해도 좋다. 단 저작권 표시 및 이 허가 표시를 소프트웨어의 모든 복제물 또는 중요한 부분에 기재해야 한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"저자 또는 저작권자는 소프트웨어에 관해서 아무런 책임을 지지 않는다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"MIT 라이선스가 표기되어 있다는 것"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"수정 및 배포가 자유롭다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"실무 혹은 상업적으로 이용해도 된다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"하지만 이 소스로 인한 피해는 전적으로 사용자가 감수해야 된다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"내 앱을 만들때와는 달리 모듈 밖에서 내 코드를 어떻게 사용할지에 대한 고민도 많이 해야한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"내 프로젝트 안에서야 private, fileprivate 정도를 고민하지만 오픈 소스 라이브러리를 만드려면 다른 프로그래머가 내 라이브러리 모듈 내부의 코드를 어느 수준까지 사용하게 할지도 고려해야 한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"스위프트 언어의 접근수준에 대한 이해가 필요하다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"코코아팟 톺아보기"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Swift 및 Objective-C 언어 환경 프로젝트의 의존성을 관리해주는 도구"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"7만개 이상의 라이브러리를 보유하고 있고 300만개 이상의 애플리케이션에서 코코아팟을 활용중"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Podfile에 사용할 라이브러리만 명시해주면 자유롭게 프로젝트에서 사용할 수 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"코코아팟 설치하기"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"braw install cocoapods","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"코코아팟 초기화","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"프로젝트를 생성 후 프로젝트 폴더에서 아래 명령어를 사용한다","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"pod init"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"프로젝트 폴더 내부에 생성된 podfile을 열어서 use_frameworks! 아래부터 설치할 pod을 명시해준다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/jsMoCMz.png","type":"image"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"pod을 명시해주고 난 뒤에 파일을 저장한다. 이후 아래 명령어를 사용한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"pod install"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"위 명령어를 실행하면 명시된 pod 라이브러리들을 프로젝트로 받아오게 된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/wSETEfY.png"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이 후부터는 .xcworkspace 프로젝트 파일을 열어 작업을 해야한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":".xcworkspace는 .xcodeproj의 모음, 즉 Xcode 프로젝트를 워크스페이스라는 하나의 작업공간으로 묶어놓은 단위이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그래서 코코아팟을 통해 라이브러리를 가져오면 기존 프로젝트와 의존성을 맺어주기 위해 워크스페이스를 생성해주는 것이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Pods 프로젝트가 전역으로 관리되는 것은 아니다","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"워크스페이스에 여러개의 프로젝트가 포함되어 있다면 podfile에서 각 프로젝트에 원하는 라이브러리 의존성을 추가하여 개별적으로 사용할 수도 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이제 사용하고 싶은 라이브러리를 해당 프로젝트에서 import하여 사용하면 된다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"import Alamofire"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Podfile?"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"최신 버전의 라이브러리를 사용하고 싶다면 따로 pod 버전을 명시하지 않으면 된다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"pod ‘Alamofire’"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"한 버전을 특정할 수도 있다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"pod ‘Alamofire’, ‘1.0’"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"버전에 대하여 조건을 명시할 수도 있다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"> 0.1 0.1","type":"codeVoice"},{"text":" 보다 높은 버전","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":">= 0.1 0.1"},{"text":" 이고 보다 높은 버전","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"< 0.1 0.1"},{"type":"text","text":" 보다 낮은 버전"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"<= 0.1 0.1","type":"codeVoice"},{"type":"text","text":" 이고 보다 낮은 버전"}]}]}]}]},{"content":[{"inlineContent":[{"text":"부등호 이외에 ~> 연산자를 활용할 수도 있다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"~> 0.1.2","type":"codeVoice"},{"text":" 0.1.2 이상이지만 0.2 보다는 낮은 버전 (0.2는 포함하지 않음)","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"~> 0.1"},{"text":" 0.1 이상이지만 1.0 보다는 낮은 버전 (1.0은 포함하지 않음)","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"~> 0"},{"type":"text","text":" 실질적으로 포함시키지 않는다는 의미
버전 정책에 대한 좀 더 자세한 사항은 Semantic Versioning 을 참고하자."}]}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Podfile.lock?","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"pod들의 버전을 계속 추적하여 기록해놓고 유지시키는 역할을 한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"유일성을 보증하는 해쉬값인 CHECKSUM이 부여된다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"만약 pod버전에 하나라도 변화가 생긴다면 CHECKSUM 또한 변하게 된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 경우 Git을 사용할 경우 Podfile.lock이 diff로 잡히게 된다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"CHECKSUM이 변함없이 유지된다는 것 == 협업 환경에서 pod 버전을 모두 동일하게 사용하고 있음을 의미"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 pod 버전을 업데이트 했다면 Podfile.lock도 같이 커밋해야한다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"명령어"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"pod install"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"맨 처음 pod을 세팅하기 위해 사용한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Podfile에 pod을 추가, 수정, 삭제 할 때에도 사용한다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"pod update"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"해당 팟의 업데이트된 버전이 있는지 검색한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Podfile.lock은 참조하지 않는다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 명령어는 팟을 최신버전으로 업데이트 시켜주는 것"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"단 podfile에 명시해둔 pod의 버전조건과 일치해야 한다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"실행하면 코코아팟은 모든 현재 프로젝트의 팟에 대해 Podfile에 명시해둔 버전조건에 부합하는 최신버전으로 업데이트를 실행한다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"pod outdated"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Podfile.lock에 리스트된 것 보다 새로운 버전을 가진 모든 팟을 나열해준다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이 팟들에 대해 pod update {팟이름} 을 실행한다면 업데이트가 될 것이라는 것을 의미한다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"code":"pod repo update","type":"codeVoice"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"\/Users\/{사용자이름}\/.cocoapods\/repos 에 있는 모든 podspec 파일을 업데이트","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"카르타고 톺아보기"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"Brew install carthage 명령어로 카르타고 설치","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"명령어"}]},{"items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"carthage update"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"카르타고를 기반으로 모든 의존성을 갱신하고 빌드한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"특정 프레임워크만 업데이트 하기를 원한다면 뒤에 이름만 기입해주면 된다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"carthage bootstrap"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Cartfile.resolved를 기반으로 모든 의존성을 갱신하고 빌드한다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"carthage build","type":"codeVoice"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"갱신하지 않고 모든 의존성을 빌드한다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"carthage outdated"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"새로운 버전이 있는 항목들을 알려준다.","type":"text"}]}]}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"text":"이후 남은 챕터 다시 뿌시러 가야한다…","anchor":"%EC%9D%B4%ED%9B%84-%EB%82%A8%EC%9D%80-%EC%B1%95%ED%84%B0-%EB%8B%A4%EC%8B%9C-%EB%BF%8C%EC%8B%9C%EB%9F%AC-%EA%B0%80%EC%95%BC%ED%95%9C%EB%8B%A4","level":3,"type":"heading"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"https:\/\/neph3779.github.io\/til\/2021.06.03-TIL\/","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/lxxyeon.tistory.com\/110"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/zeddios.tistory.com\/1308","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/yagom.net\/courses\/open-source-library\/","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"sections":[],"metadata":{"roleHeading":"Article","title":"211220 의존성 관리도구, 오픈소스","modules":[{"name":"Ari's Notes"}],"role":"article"},"references":{"doc://AriNote/documentation/AriNote/Xcode":{"role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Xcode","url":"\/documentation\/arinote\/xcode","title":"Xcode","kind":"article","abstract":[{"text":"언젠가…. 좋아질거지 Xcode…?","type":"text"}],"type":"topic"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/jsMoCMz.png":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/jsMoCMz.png"}],"identifier":"https:\/\/i.imgur.com\/jsMoCMz.png","alt":null},"https://i.imgur.com/wSETEfY.png":{"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/wSETEfY.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/wSETEfY.png","alt":null}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211221_collectionview,_unit_test.json b/docs/data/documentation/arinote/211221_collectionview,_unit_test.json new file mode 100644 index 0000000..6bb635b --- /dev/null +++ b/docs/data/documentation/arinote/211221_collectionview,_unit_test.json @@ -0,0 +1 @@ +{"schemaVersion":{"minor":3,"major":0,"patch":0},"metadata":{"title":"211221 CollectionView, Unit Test","roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}]},"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211221_CollectionView,_Unit_Test","interfaceLanguage":"swift"},"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","level":1,"anchor":"TIL-Today-I-Learned","type":"heading"},{"type":"paragraph","inlineContent":[{"text":"12월 21일 (화)","type":"text"}]},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용","level":2,"type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"은행창구매니저 STEP 1 PR, STEP 2 설계"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UICollectionView?","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","level":2,"type":"heading"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[private한 속성에 접근하여 테스트하는 것이 올바른 것일까?]"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Queue나 LinkedList를 타입으로 구현하게 되면서 Node나 head, tail등 접근하여 테스트 하는 경우가 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"나랑 허황도 해당 문제 때문에 고민을 했었는데… 요한, 아카펠라, 호박네 팀도 같은 고민을 하고 있었다. 고민을 엿듣다가 다시 생각해보게 되었다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"직접 속성값을 접근하여 테스트 결과를 확인해보는 것이 올바른 것인지 의문이 들었다."}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"오늘 다시 고민해보니 적절치 못하다는 생각이 들었다. 그 이유는 애초에 접근하지 못하도록 만든 속성값을 접근하여 테스트 결과를 확인한다는 것이… 말이 안되기 때문이다. 접근하지 못하는 프로퍼티는 테스트가 불가능한 것이 맞다는 생각이 들었다. 따라서 접근할 수 있는 프로퍼티나 메소드를 활용하여 최소한으로 테스트를 진행하는 것이 맞다는 판단이 들었다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"나랑 허황은 해당 문제를 해결하기 위해 Mock 타입을 따로 구현하였다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"하지만 이 방법은 만약 LinkedList에 기능이 추가된다면 Mock에도 기능이 추가되어야하는데, 똑같이 기능이 추가된다는 보장이 없다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 Mock을 쓰는 것은 적절하지 않다 라는 생각이 들었다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"기능을 준수하게 만드는 프로토콜을 생성한다면 이 문제를 해결할지도 모르겠다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Mock타입, 실제 타입 둘다 프로토콜을 준수하게 만든다면 추후 기능이 추가되더라도 프로토콜을 수정할 것이니 둘다 수정할 수 있을 것이다."}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"하지만 테스트를 통해 알고싶은 것은 상태값인데, 그렇다면 상태값을 반환하는 프로퍼티나 메소드를 추가로 만들어야 하는 것일까?","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"테스트를 위한 코드이기 때문에… 그것이 걸렸다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"테스트 외에 사용하지 않는 기능이라면 구지 추가해줄 필요가 있을까..?","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그렇다면 인덱스를 통해 값을 접근할 수 있도록 Subscript를 활용하는 것은 어떨까?"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이것또한 테스트를 위해서만 활용될 거라는 추측 때문에… 좋은 방법인지는 확신이 서질 않았다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 부분에 대해서 리뷰어에게 추가적으로 조언을 구해보면 좋을 것 같다."}]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[컬렉션뷰]"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"컬렉션뷰란?"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"유연하고 변경 가능한 레이아웃을 사용하여 데이터 아이템의 정렬된 세트를 표시하는 수단이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"가장 일반적인 용도는 데이터 아이템을 그리드와 같은 형태로 표현한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/2vYHtLg.png"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"컬렉션뷰의 구성요소","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"셀(cell)"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"컬렉션뷰의 주요 콘텐츠를 표시한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컬렉션뷰는 컬렉션 데이터 소스 객체에서 표시할 셀에 대한 정보를 가져온다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"각 셀은 "},{"code":"UICollectionViewCell","type":"codeVoice"},{"type":"text","text":" 클래스의 인스턴스 또는 "},{"code":"UIColletionViewCell","type":"codeVoice"},{"text":"을 상속받은 클래스의 인스턴스이다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"보충 뷰(Supplementary views)","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"섹션에 대한 정보를 표시한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"셀과 달리 보충 뷰는 필수는 아니며, 사용법과 배치 방식은 사용되는 레이아웃 객체가 제어한다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"헤더와 푸터를 예로 들 수 있다."}]}]}]}]}]}]},{"content":[{"inlineContent":[{"text":"데코레이션 뷰(Decoration views)","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"콘텐츠가 스크롤 되는 컬렉션뷰에 대한 배경을 꾸밀 때 사용한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"레이아웃 객체는 데코레이션 뷰를 사용하여 커스텀 배경 모양을 구현할 수 있다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"레이아웃 객체(Layout Object)"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컬렉션뷰 내의 아이템 배치 및 시각적 스타일을 결정한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컬렉션뷰 데이터소스 객체가 뷰와 표시할 콘텐츠를 제공한다면, 레이아웃 객체는 크기, 위치 및 해당 뷰의 레이아웃과 관련된 특성들을 결정한다."}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컬렉션뷰 구현을 이루기 위한 클래스 및 프로토콜"}]},{"items":[{"content":[{"inlineContent":[{"text":"최상위 포함 및 관리(Top level containment)","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"UICollectionView","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"컬렉션 뷰의 콘텐츠가 보이는 영역을 정의한다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"UICollectionViewController"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컬렉션 뷰를 관리하는 뷰 컨트롤러이다. 선택적으로 사용할 수 있다."}]}]}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"콘텐츠 관리"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"UICollectionViewDataSource"}]},{"items":[{"content":[{"inlineContent":[{"text":"컬렉션뷰와 관련된 중요한 객체이며 필수적으로 제공해야 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컬렉션뷰의 콘텐츠를 관리하고 해당 콘텐츠를 표시하기 위한 뷰를 제공한다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"UICollectionViewDelegate"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"사용자와의 상호작용과 셀 강조 표시 및 선택 등을 관리한다.","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"표시(Presentation)"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"UICollectionReusableView","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컬렉션에 표시된 모든 뷰는 해당 클래스의 인스턴스여야 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"컬렉션뷰에서 사용중인 뷰 재사용 매커니즘을 지원한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"새로운 뷰를 만드는 대신 뷰를 재사용하여 성능을 향상 시킨다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"UICollectionViewCell"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"인스턴스에 제공되는 데이터를 화면에 표시하는 역할을 담당한다.","type":"text"}]}]}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"레이아웃(Layout)"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"UICollectionViewLayout"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"해당 클래스의 서브클래스는 레이아웃 객체라고 하며 컬렉션뷰 내부의 셀 및 재사용 가능한 뷰의 위치, 크기 및 시각적 속성을 정의한다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"UICollectionViewAttribute"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"컬렉션뷰 내의 지정된 아이템의 레이아웃 관련 속성을 관리한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"레이아웃 프로세스 중에 컬렉션뷰에 셀과 재사용 가능한 뷰를 표시하는 위치와 방법을 알려준다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"레이아웃 객체 아이템이 삽입, 삭제 혹은 컬렉션 뷰 내에서 이동할 때마다 레이아웃 객체는 UICollectionViewUpdateItem의 인스턴스를 받는다.","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"플로우 레이아웃(Flowlayout)","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"UICollectionViewFlowLayout","type":"codeVoice"},{"text":" \/ ","type":"text"},{"code":"UICollectionViewDelegatFlowLayout","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"그리드 혹은 다른 라인기반(lined-based) 레이아웃을 구현하는 데 사용된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"클래스를 그대로 사용하거나 동적으로 커스터마이징할 수 있는 플로우 델리게이트 객체와 함께 사용할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"UICollectionViewFlowLayout"},{"type":"text","text":"는 컬렉션뷰를 위한 디폴트 클래스로, 그리드 스타일로 셀들을 배치하도록 설계되어있다. scrollDirection 프로퍼티를 통해 수평 및 수직 스크롤을 지원한다."}]}]}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[컬렉션뷰 셀]"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"컬렉션뷰 셀이란?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컬렉션뷰에서 데이터를 화면에 표시하기 위해 사용된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"냉장고 속에 있는 반찬통으로 생각할 수 있다. 컬렉션뷰라는 냉장공가 있고, 냉장고 안에는 실제 반찬(콘텐츠)을 담고 있는 컬렉션뷰 셀이라는 반찬통이 있다고 생각할 수 있다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"특징"}]},{"items":[{"content":[{"inlineContent":[{"text":"데이터 아이템을 화면에 표시한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하나의 셀은 하나의 데이터 아이템을 화면에 표시한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"두개의 배경을 표시하는 뷰와 하나의 콘텐츠를 표시하는 뷰로 구성되어 있다. 두개의 배경뷰는 셀이 선택되었을 때 사용자에게 시각적인 표현을 제공하기 위해 사용된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컬렉션뷰의 레이아웃 객체에 의해 관리된다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"뷰의 재사용 메커니즘을 지원한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"일반적으로 컬렉션뷰 셀 클래스의 인스턴스는 직접 생성하지 않는다. 대신 특정 셀의 하위 클래스를 컬렉션뷰 객체에 등록한 후 컬렉션뷰 셀 클래스의 새로운 인스턴스가 필요할 때, 컬렉션의 "},{"type":"codeVoice","code":"dequeueReusavleCell(withReuseIdectifier:for:)"},{"type":"text","text":" 메소드를 호출한다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"스토리보드를 활용할 때에는 컬렉션뷰에 따로 셀 클래스를 등록할 필요는 없다."}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"UICollectionViewCell"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"셀의 구성요소 관련 프로퍼티"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"var contentView: UIView"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"셀의 콘텐츠를 표시하는 뷰"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"var backgroundView: UIView?"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"셀의 배경을 나타내는 뷰"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"셀이 처음 로드되었을 경우 셀이 강조 표시되지 않거나 선택되지 않을 때 항상 기본배경의 역할을 한다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"var selectedBackgroundView: UIView?","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"셀이 선택되었을 때 배경뷰 위에 표시되는 뷰","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"셀이 강조 표시되거나 선택될 때마다 기본 배경 뷰인 backgroundView를 대체하여 표시된다.","type":"text"}]}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"셀의 상태 관련 프로퍼티","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"code":"var isSelected: Bool","type":"codeVoice"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"셀이 선택되었는지를 나타낸다."}]}]},{"content":[{"inlineContent":[{"text":"셀이 선택되어있지 않다면 false다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"var isHighlighted: Bool"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"셀의 하이라이트 상태를 나타낸다","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"하이라이트 되어있지 않다면 기본값은 false다."}],"type":"paragraph"}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"셀의 드래그 상태 관련 메서드","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"code":"func dragStateDidChange(_:)","type":"codeVoice"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"셀의 드래그 상태가 변경되면 호출된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"드래그 상태는 UICollectionViewCellDragState의 열거형으로 표현된다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"None, lifting, dragging의 3가지 상태를 갖는다.","type":"text"}]}]}],"type":"unorderedList"}]}]}]}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"컬렉션뷰 셀 vs 테이블뷰 셀"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테이블 뷰 셀의 구조는 콘텐츠 영역과 액세서리뷰 영역으로 나뉘었지만, 컬렉션뷰 셀은 배경뷰와 실제 콘텐츠를 나타내는 콘텐츠 뷰로 나뉘어져있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"테이블뷰 셀은 기본으로 제공되는 특정 스타일을 적용할 수 있지만 컬렉션뷰 셀은 특정한 스타일이 없다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테이블뷰 셀은 목록형태로만 레이아웃 되지만, 컬렉션뷰 셀은 다양한 레이아웃을 지원한다."}]}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[DataSource와 Delegate]","type":"text"}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/5FdeptX.png"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터소스(DataSource)"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"가장 중요한 객체이며, 필수로 제공해야 한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"컬렉션뷰의 콘텐츠(데이터)를 관리하고 해당 콘텐츠를 표현하는 데 필요한 뷰를 만든다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"데이터소스 객체를 구현하려면 UICollectionViewDataSource 프로토콜을 준수하면 된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"그리고 데이터소스 객체는 최소한 "},{"type":"codeVoice","code":"collectionView(:numberOfItemsInSection:)"},{"type":"emphasis","inlineContent":[{"text":"과","type":"text"}]},{"text":" ","type":"text"},{"code":"collectionView(:cellForItemAt:)","type":"codeVoice"},{"type":"text","text":" 메서드를 구현해야 하며 나머지는 선택사항이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"필수 메소드"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"지정된 섹션에 표시할 항목의 개수를 묻는 메소드"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컬렉션뷰의 지정된 위치에 표시할 셀을 요청하는 메소드"}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"주요 선택 메소드"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"optional func numberOfSections(in collectionView: UICollectionView) -> Int"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"컬렉션뷰의 섹션의 개수를 묻는 메소드"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"구현하지 않으면 섹션 개수 기본값은 1이다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"optional func collectionView(_ collectionView: UICollectionView, canMoveItemAt indexPath: IndexPath) -> Bool"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"지정된 위치의 항목을 컬렉션뷰의 다른 위치로 이동할 수 있는지를 묻는 메소드"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"optional func collectionView(_ collectionView: UICollectionView, moveItemAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath)"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"지정된 위치의 항목을 다른 위치로 이동하도록 지시하는 메소드"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"델리게이트(Delegate)"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"컬렉션뷰에서 셀의 선택 및 강조표시를 관리하고 해당 셀에 대한 작업을 수행할 수 있는 메소드를 정의한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"주요 메소드","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"optional func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"지정된 셀이 사용자에 의해 선택될 수 있는지 묻는 메소드"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"선택이 가능한 경우 true로 응답하며 아닌경우는 false로 응답한다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"optional func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath)","type":"codeVoice"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"지정된 셀이 선택되었음을 알리는 메소드"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"optional func collectionView(_ collectionView: UICollectionView, shouldDeselectItemAt indexPath: IndexPath) -> Bool","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"지정된 셀의 선택이 해제될 수 있는지 묻는 메소드","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"선택해제가 가능한 경우 true로 응답하며, 그렇지 않다면 false로 응답한다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"optional func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath)","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"지정된 셀의 선택이 해제되었음을 알리는 메소드","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"optional func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"지정된 셀이 강조될 수 있는지 묻는 메소드.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"강조해야하는 경우 true로 응답하며 그렇지 않다면 false로 응답"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"optional func collectionView(_ collectionView: UICollectionView, didHighlightItemAt indexPath: IndexPath)","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"지정된 셀이 강조되었을 때 알려주는 메소드"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"optional func collectionView(_ collectionView: UICollectionView, didUnhighlightItemAt indexPath: IndexPath)"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"지정된 셀이 강조가 해제될 때 알려주는 메소드"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"https:\/\/www.boostcourse.org\/mo326\/lecture\/16906?isDesc=false","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}],"variants":[{"paths":["\/documentation\/arinote\/211221_collectionview,_unit_test"],"traits":[{"interfaceLanguage":"swift"}]}],"sections":[],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/5FdeptX.png":{"alt":null,"identifier":"https:\/\/i.imgur.com\/5FdeptX.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/5FdeptX.png"}],"type":"image"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/2vYHtLg.png":{"alt":null,"identifier":"https:\/\/i.imgur.com\/2vYHtLg.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/2vYHtLg.png"}],"type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211222_uicollectionviewflowlayout,_pop.json b/docs/data/documentation/arinote/211222_uicollectionviewflowlayout,_pop.json new file mode 100644 index 0000000..713b5f1 --- /dev/null +++ b/docs/data/documentation/arinote/211222_uicollectionviewflowlayout,_pop.json @@ -0,0 +1 @@ +{"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211222_UICollectionViewFlowLayout,_POP","interfaceLanguage":"swift"},"schemaVersion":{"major":0,"patch":0,"minor":3},"metadata":{"modules":[{"name":"Ari's Notes"}],"title":"211222 UICollectionViewFlowLayout, POP","role":"article","roleHeading":"Article"},"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","level":1,"anchor":"TIL-Today-I-Learned","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"12월 22일 (수)"}]},{"type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"은행창구매니저 STEP 1 피드백 확인 후 개선","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"UICollectionViewFlowLayout 톺아보기"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Protocol Oriented Programming 예습하기","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"heading","text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[Protocol Oriented Programming]"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Protocol in Objective-C","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"단지 기능의 청사진의 역할을 수행"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"주로 Delegate, DataSource 등으로 이용"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기본 구현(Default Implementation) 불가"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"카테고리 적용 불가"}]}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Protocol in Swift"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Objective-C의 프로토콜 기능은 기본"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"기본 구현(Default Implementation) 가능","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Protocol + Extension = Protocol extension"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"특정 타입이 할 일 지정 + 구현을 한 방에!","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Protocol extension"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"둘을 함께 사용한다면 공통된 기능을 채택하여 사용할 수 있게 해줄 수가 있다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Idea from…","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"상속의 한계"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"서로 다른 클래스에서 상속받은 클래스는 동일한 기능을 구현하기 위해 중복 코드 발생","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"카테고리의 한계 및 부작용","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"프로퍼티 추가 불가","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"오직 클래스에만 적용 가능"}]}]},{"content":[{"inlineContent":[{"text":"기존 메서드를 (자신도 모르게) 오버라이드 가능","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참조타입의 한계"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"동적 할당과 참조 카운팅에 많은 자원 소모"}]}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로토콜을 활용하여 기능을 조립할 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/LXDZBEo.png"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"여기서 Extension을 사용하여 protocol을 구현해준다면 기능을 갖다 붙이는 것과 같은 효과를 줄 수 있게 된다."}],"type":"paragraph"}]},{"content":[{"type":"heading","level":3,"anchor":"POP--Value-in-Project","text":"POP & Value in Project"}]},{"content":[{"inlineContent":[{"type":"text","text":"테이블뷰 셀을 클릭하면 디테일뷰가 나오는 구성"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"사진이 여러개 나오는 뷰도 추가해주면 안될까요?","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"CollectionView","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]}]},{"syntax":"swift","type":"codeListing","code":["protocol MediaContainer: class {"," var content: Content? { get set }"," var media: UIImageView { get }"," var note: UILabel { get set }"," "," func contentChanged()","}","","extension MediaContainer {"," func contentChanged() {"," \/\/ Update view..."," }","}","","class TimelineTableViewCell: UITabelViewCell, MediaContainer { "," var media: UIImageView!"," var note: UILabel!"," var content: Content? {"," didSet {"," contentChanged()"," }"," }","}","","class TimelineCollectionViewCell: UITabelViewCell, MediaContainer { "," var media: UIImageView!"," var note: UILabel!"," var content: Content? {"," didSet {"," contentChanged()"," }"," }","}","","class TimelineDetailView: MediaContainer { "," var media: UIImageView!"," var note: UILabel!"," var content: Content? {"," didSet {"," contentChanged()"," }"," }","}"]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"MediaContainer 프로토콜을 가져다 쓰는 것만으로도 기능을 공통적으로 활용할 수 있다는 장점이 있다."}],"type":"paragraph"}]},{"content":[{"type":"heading","text":"Controller with POP & Value","anchor":"Controller-with-POP--Value","level":3}]},{"content":[{"inlineContent":[{"text":"셀을 클릭할 때 다음 화면으로 넘어가는 부분","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이런 공통된 기능을 protocol과 extension을 활용하여 정의해줄 수도 있다."}]}]}]}]},{"content":[{"type":"heading","anchor":"Protocol%EA%B3%BC-extension%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%A9%B4%EC%84%9C-%EC%96%BB%EC%9D%84-%EC%88%98-%EC%9E%88%EB%8A%94-%EC%9D%B4%EC%A0%90","text":"Protocol과 extension을 사용하면서 얻을 수 있는 이점","level":3},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"범용적인 사용","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"클래스, 구조체, 열거형 등등 모든 타입에 적용 가능","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"제네릭과 결합하면 더욱 파급적인 효과 (Type safe & Flexible code)"}]}]}]}]},{"content":[{"inlineContent":[{"text":"상속의 한계 극복","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"특정 상속 체계에 종속되지 않음","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"프레임워크에 종속적이지 않게 재활용 가능"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"적은 시스템 비용"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Reference type cost > Value type cost"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"용이한 테스트"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"GUI 코드 없이도 수월한 테스트","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"text":"한계점","type":"heading","level":3,"anchor":"%ED%95%9C%EA%B3%84%EC%A0%90"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"자주 사용되는 Delegate, DataSource 등 프레임워크 프로토콜에는 기본 구현이 불가능 하다. (코코아터치 프레임워크…)"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Objective-C Protocol + Swift Extension은 기본 구현(Default Implementation)이 불가능하기 때문이다.","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"text":"그럼에도 사용하는 이유","type":"heading","level":3,"anchor":"%EA%B7%B8%EB%9F%BC%EC%97%90%EB%8F%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Value Type을 사용하여 성능상의 이득을 취하자","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"protocol + extension + Generic은 환상의 조합","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"이제 상속을 통한 수직 확장이 아닌 protocol과 extension을 통한 수평 확장과 기능추가를 고민해볼 때…","type":"text"}],"type":"paragraph"}]}]}]},{"content":[]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[UICollectionViewFlowLayout]"}]}]},{"items":[{"content":[{"inlineContent":[{"text":"컬렉션뷰의 셀을 원하는 형태로 정렬할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"레이아웃 객체가 셀을 선형 경로에 배치하고 최대한 이 행을 따라 많은 셀을 채우는 것을 의미한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"현재 행에서 레이아웃 객체의 공간이 부족하면 새로운 행을 생성하고 거기에서 레이아웃 프로세스를 계속 진행한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/hKdr9vV.png","type":"image"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"플로우 레이아웃 수직 스크롤"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/7GAHUlO.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"플로우 레이아웃 수평 스크롤","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/5dBifiZ.png"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"플로우 레이아웃을 사용해 그리드 형태뿐만 아니라 다양한 레이아웃을 구현할 수 있다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"셀을 하나의 행으로 만들어 정렬한 후 간격을 조정할 수도 있다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"플로우 레이아웃 단일 행","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/YXtKgyl.png"}]}]},{"content":[{"inlineContent":[{"text":"FlowLayout 구성 단계","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"플로우 레이아웃 객체를 작성해 컬렉션뷰의 레이아웃 객체로 지정한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"셀의 너비와 높이를 구성한다."}]}]},{"content":[{"inlineContent":[{"text":"필요한 경우 셀의 간격을 조절한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"원할 경우 섹션 헤더 혹은 섹션 푸터의 크기를 지정할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"레이아웃의 스크롤 방향을 설정한다."}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Tip"},{"type":"text","text":" "},{"text":"플로우 레이아웃은 대부분 프로퍼티의 기본값을 가지고 있다. 하지만 셀의 너비와 높이는 모두 0으로 지정되어 있기 때문에 셀의 크기는 지정해주어야 한다. 그렇지 않을 경우 셀의 너비와 높이의 기본값이 0이기 때문에 셀이 화면에 보이지 않을 수도 있다.","type":"text"}]}],"style":"note","type":"aside","name":"Note"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"FlowLayout 속성 변경","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"콘텐츠 모양을 구성하기 위해 여러가지 프로퍼티를 제공"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"적절한 값을 설정하면 모든 셀에 동일한 레이아웃이 적용된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"예시로 itemSize 프로퍼티를 사용하여 셀의 크기를 설정할 경우 모든 셀의 크기가 동일하게 적용된다."}]}]}]}]},{"content":[{"inlineContent":[{"text":"FlowLayout 셀 크기 지정하기","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"셀이 모두 같은 크기를 가질 경우 itemSize의 프로퍼티에 적절한 너비와 높이 값을 할당한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"각각 셀마다 다른 크기를 지정하려면 Delegate에서 "},{"code":"collectionView(_:layout:sizeForItemAt:)","type":"codeVoice"},{"type":"text","text":" 메소드를 구현해야 한다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"메소드의 매개변수로 제공하는 인덱스 경로 정보를 사용해 해당셀의 크기를 반환할 수 있다."}]}]}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/Y2yZwgx.png","type":"image"}],"type":"paragraph"}]}]},{"type":"aside","style":"note","name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"Tip","type":"text"},{"type":"text","text":" "},{"type":"text","text":"셀마다 다른 크기를 지정하게 되면 행에 있는 셀의 수는 행마다 달라질 수 있다."},{"text":" ","type":"text"},{"type":"image","identifier":"https:\/\/i.imgur.com\/o0tWeH8.png"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"셀 및 행의 사이 간격 지정하기","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"같은 행의 셀 사이의 최소 간격과 연속하는 행 사이의 최소 간격을 지정할 수 있다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"최소 간격이라는 점을 명심하자.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"행끼리 간격은 플로우 레이아웃 객체에서 셀끼리의 간격에서와 같은방법을 사용한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"모든 셀의 크기가 같다면 행 간격의 최솟값을 절대적으로 수용하며 하나의 행에 있는 모든 셀이 다음 행의 셀과 균등한 간격을 유지할 수 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"셀의 크기가 동일한 경우","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/4zFjnUO.png"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"셀의 크기가 다른 경우 셀의 간격"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/xNI5bJN.png"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"콘텐츠 여백 수정하기","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"섹션 Inset은 셀을 배치할 때 여백공간을 조절하는 방법의 하나이다."}]}]},{"content":[{"inlineContent":[{"text":"Inset을 사용해 섹션 헤더뷰 다음과 푸터뷰 앞에 공간을 삽입할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"콘텐츠의 면 주위에 공간을 삽입할 수도 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Inset은 셀 배치에 있어서 사용가능한 공간을 줄이기 때문에 이를 사용하여 주어진 행의 셀 수를 제한할 수도 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/bBlsBDR.png","type":"image"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"셀 예상(Estimated) 크기 지정"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"UICollectionViewFlowLayout 클래스의 itemSize 프로퍼티를 이용해 모든 셀을 같은 크기로 설정하는 방법"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UICollectionViewDelegateFlowLayout 프로토콜의 "},{"type":"codeVoice","code":"collectionView(_:layout:sizeForItemAt:)"},{"type":"text","text":" 메소드를 사용해 셀마다 다른크기를 지정하는 방법"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"셀에 오토레이아웃을 적용하고 셀 스스로 크기를 결정한 후 이를 UICollectionViewLayout 객체에 알려준다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이 방법을 사용하려면 estimatedItemSize 프로퍼티를 사용해 대략적인 셀의 최소 크기를 미리 알려준다.","type":"text"}]}]}],"type":"unorderedList"}]}]}]}],"type":"unorderedList"},{"syntax":"swift","code":["let flowLayout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()","flowLayout.estimatedItemSize = CGSize(width: 50.0, height: 50.0)","","collectionView.collectionViewLayout = flowLayout"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UICollectionViewDelegateFlowLayout"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"UICollectionViewFlowLayout과 상호작용하여 레이아웃을 조정할 수 있는 메소드가 정의되어 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"셀의 크기와 셀 간의 사이 간격을 정의한다."}],"type":"paragraph"}]}]}]}]},{"type":"codeListing","syntax":"swift","code":["\/\/ 지정된 셀의 크기를 반환하는 메소드","optional func collectionView(_ collectionView: UICollectionView, "," layout collectionViewLayout: UICollectionViewLayout, "," sizeForItemAt indexPath: IndexPath) -> CGSize","","\/\/ 지정된 섹션의 여백을 반환하는 메소드","optional func collectionView(_ collectionView: UICollectionView, "," layout collectionViewLayout: UICollectionViewLayout, "," insetForSectionAt section: Int) -> UIEdgeInsets","","\/\/ 지정된 섹션의 행 사이 간격 최소 간격을 반환하는 메소드.","\/\/ scrollDirection이 horizontal이면 수직이 행이 되고 vertical이면 수평이 행이 된다.","optional func collectionView(_ collectionView: UICollectionView, "," layout collectionViewLayout: UICollectionViewLayout, ","minimumLineSpacingForSectionAt section: Int) -> CGFloat","","\/\/ 지정된 섹션의 셀 사이의 최소 간격을 반환하는 메소드","optional func collectionView(_ collectionView: UICollectionView, "," layout collectionViewLayout: UICollectionViewLayout, ","minimumInteritemSpacingForSectionAt section: Int) -> CGFloat","","\/\/ 섹션의 헤더 크기를 반환하는 메소드","optional func collectionView(_ collectionView: UICollectionView, "," layout collectionViewLayout: UICollectionViewLayout, ","referenceSizeForHeaderInSection section: Int) -> CGSize","","\/\/ 섹션의 푸터 크기를 반환하는 메소드","optional func collectionView(_ collectionView: UICollectionView, "," layout collectionViewLayout: UICollectionViewLayout, ","referenceSizeForFooterInSection section: Int) -> CGSize"]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/www.boostcourse.org\/mo326\/lecture\/16906?isDesc=false"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/library\/archive\/documentation\/WindowsViews\/Conceptual\/CollectionViewPGforIOS\/UsingtheFlowLayout\/UsingtheFlowLayout.html"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/library\/archive\/documentation\/WindowsViews\/Conceptual\/CollectionViewPGforIOS\/CreatingCustomLayouts\/CreatingCustomLayouts.html#\/\/apple_ref\/doc\/uid\/TP40012334-CH5-SW1"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uicollectionviewdelegateflowlayout\/1617708-collectionview","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/uicollectionviewdelegateflowlayout"}],"type":"paragraph"}]}]}]}]}]}],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211222_uicollectionviewflowlayout,_pop"]}],"sections":[],"references":{"https://i.imgur.com/4zFjnUO.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/4zFjnUO.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/4zFjnUO.png","type":"image"},"https://i.imgur.com/o0tWeH8.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/o0tWeH8.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/o0tWeH8.png","type":"image"},"https://i.imgur.com/Y2yZwgx.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/Y2yZwgx.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/Y2yZwgx.png","type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/7GAHUlO.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/7GAHUlO.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/7GAHUlO.png","type":"image"},"https://i.imgur.com/LXDZBEo.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/LXDZBEo.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/LXDZBEo.png","type":"image"},"https://i.imgur.com/hKdr9vV.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/hKdr9vV.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/hKdr9vV.png","type":"image"},"https://i.imgur.com/5dBifiZ.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/5dBifiZ.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/5dBifiZ.png","type":"image"},"https://i.imgur.com/bBlsBDR.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/bBlsBDR.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/bBlsBDR.png","type":"image"},"https://i.imgur.com/YXtKgyl.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/YXtKgyl.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/YXtKgyl.png","type":"image"},"https://i.imgur.com/xNI5bJN.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/xNI5bJN.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/xNI5bJN.png","type":"image"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211223_pop,_delegate.json b/docs/data/documentation/arinote/211223_pop,_delegate.json new file mode 100644 index 0000000..513c797 --- /dev/null +++ b/docs/data/documentation/arinote/211223_pop,_delegate.json @@ -0,0 +1 @@ +{"metadata":{"modules":[{"name":"Ari's Notes"}],"role":"article","title":"211223 POP, delegate","roleHeading":"Article"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211223_pop,_delegate"]}],"schemaVersion":{"major":0,"patch":0,"minor":3},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"primaryContentSections":[{"kind":"content","content":[{"level":1,"text":"TIL (Today I Learned)","type":"heading","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"type":"text","text":"12월 23일 (목)"}]},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"text":"학습 내용","type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"은행창구매니저 STEP 2 진행","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Protocol Oriented Programming 활동학습"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"text":"고민한 점 \/ 해결 방법","level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","type":"heading"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[Protocol Oriented Programming]"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"상속은 기능을 추가, 확장을 위한 목적으로 필요하지만 다중상속이 불가능하다는 한계가 있다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"또한 클래스에서만 가능하다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로토콜 지향 프로그래밍으로 객체지향 프로그래밍의 다중상속이 불가능하다는 단점을 극복할 수 있다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"또한 프로토콜은 클래스 외에 구조체나 열거형에도 채택을 해줄 수 있다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"protocol에 extension을 구현하면 기본구현을 제공한다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"하지만 타입에서 프로토콜의 정의 부분을 다시 재구현 해준다면 타입에서의 구현이 높은 우선 순위이다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"성능을 위한다면 value type을 주로 사용하고, 꼭 필요한 경우에는 클래스를 활용하면 될 것 같다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"프로토콜 지향 프로그래밍이 확실히 객체지향보다 장점이 많지만 어떤 것이 최고다 라고 판단할 수는 없다. 상황에 따라 적합하게 활용하는 것이면 충분할 것 같다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"프로토콜을 채택한 클래스를 상속받는다면 부모 클래스가 채택한 프로토콜과 부모가 가지고 있는 기능 모두를 사용할 수 있게 된다. 이후 다른 프로토콜도 추가로 채택할 수도 있다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"장점도 많고 깔끔하고 안전해보이지만 그렇다고 남발한다면 아무곳에나 프로토콜을 가져다 쓰게 될 수도 있다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[은행 창구 매니저 프로젝트를 하면서 고민했던 점]"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"모델에서 "},{"type":"codeVoice","code":"print()"},{"type":"text","text":"해주는 부분을 분리할 수는 없을까?"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"delegate 패턴을 활용하여 분리해보는 것을 도전해보자 !!"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"codeVoice","code":"delegate 패턴"},{"type":"text","text":"을 통해서 분리해주었지만 약간 의문점이 생겼다."}],"type":"strong"}]},{"items":[{"content":[{"inlineContent":[{"text":"모델들을 print의 역할을 가져간 타입들이 참조를 해야하기 때문에 기존에 struct에서 class로 변경되었다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"역할이 확실히 분리는 되었지만 코드의 모양새가 약간 찝찝하다고 해야하나.."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"값타입이 아닌 참조타입들이라서 추후 retain count 추적 비용이 발생할 것 같다는 걱정이 들었다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이니셜라이저에 기본값을 주었었는데, main에서 한꺼번에 값을 할당해주기 위해 모조리 제거해주었다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"delegate 패턴을 구현하기 위한 위와 같은 필요에 의한 수정들이 과연 적절한 것일까?"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이 부분은 리뷰어에게 조언을 받아보기로 하였다.","type":"text"}]}]}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"느낀점"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"STEP 2에서 이러한 고민들이 좋았다고 느껴졌다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"타입 및 일반화 추상화에 더해져서 SOLID까지 고려하여 고민해볼 수 있다는 부분!","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"내가 느끼기에는 우리의 코드는 단일 책임 원칙을 잘 지켜낸 것 같다[?]","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"text":"기존에 프로젝트 진행시 요구사항에서 오버 엔지니어링 하지않도록 딱 요구사항만 지켰다면, 이번 프로젝트에서는 요구사항에서 한발 더 나아가 깊게 고민했던 것 같아서, 이런 고민을 같이 깊게 나눠준 허황에게 고마웠다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211223_POP,_delegate","interfaceLanguage":"swift"},"sections":[],"kind":"article","references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211224_escapingclosure,_autoclosure,_weak,_wildcard,_naming.json b/docs/data/documentation/arinote/211224_escapingclosure,_autoclosure,_weak,_wildcard,_naming.json new file mode 100644 index 0000000..22c0fbf --- /dev/null +++ b/docs/data/documentation/arinote/211224_escapingclosure,_autoclosure,_weak,_wildcard,_naming.json @@ -0,0 +1 @@ +{"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211224_escapingclosure,_autoclosure,_weak,_wildcard,_naming"]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211224_escapingClosure,_autoClosure,_weak,_wildcard,_naming","interfaceLanguage":"swift"},"metadata":{"title":"211224 escapingClosure, autoClosure, weak, wildcard, naming","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","level":1,"type":"heading","anchor":"TIL-Today-I-Learned"},{"inlineContent":[{"type":"text","text":"12월 24일 (금)"}],"type":"paragraph"},{"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"은행창구매니저 STEP 2 진행","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Escaping Closure"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Auto Closure","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Delegate 패턴 구현 시 네이밍]"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"허황이 공유해주었는데, 구글의 ","type":"text"},{"identifier":"https:\/\/google.github.io\/swift\/#delegate-methods","isActive":true,"type":"reference"},{"type":"text","text":"를 참고하였다고 한다."}],"type":"paragraph"}]}]},{"type":"aside","content":[{"type":"paragraph","inlineContent":[{"text":"The term “delegate’s source object” refers to the object that invokes methods on the delegate. For example, a ","type":"text"},{"type":"codeVoice","code":"UITableView"},{"text":" is the source object that invokes methods on the ","type":"text"},{"type":"codeVoice","code":"UITableViewDelegate"},{"type":"text","text":" that is set as the view’s "},{"code":"delegate","type":"codeVoice"},{"text":" property.","type":"text"}]}],"style":"note","name":"Note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"CoCoa의 프로토콜에서 영감을 받았다고 적혀있다. ","type":"text"},{"code":"UITableViewDelegate나","type":"codeVoice"},{"type":"text","text":" "},{"code":"UINavigationControllerDelegate","type":"codeVoice"},{"type":"text","text":" 프로토콜의 메소드들의 네이밍을 확인해보면 모두 "},{"type":"codeVoice","code":"tableView"},{"type":"text","text":", "},{"type":"codeVoice","code":"navigationController"},{"text":"가 앞에 꼭 붙어서 네이밍이 되어있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 대리자의 객체를 ","type":"text"},{"type":"codeVoice","code":"첫번째 argument"},{"type":"text","text":"로 사용하여 네이밍을 한다고 적혀있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"네이밍은 역시 기본 프레임워크를 참고하면 되는 것인가…? 😂","type":"text"}]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[와일드카드 패턴으로 생성한 인스턴스의 참조 카운트]"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"상황","type":"codeVoice"},{"text":" 기존에 와일드카드 패턴으로 인스턴스 생성한 타입이 Delegate를 채택하고 있던 형태였다. 이후 순환참조 문제가 우려되어 각 타입들마다 delegate 프로퍼티에 weak 키워드를 붙여주었다.","type":"text"}]},{"code":["final class Bank {"," private let bankClerk: BankClerk"," private var customerQueue = Queue()"," weak var delegate: BankDelegate?","...","}","","\/\/ main ...","func run() {"," let bankClerk = BankClerk()"," let bank = Bank(bankClerk: bankClerk)"," let bankManager = BankManager(bank: bank)"," let _ = BankClerkViewController(bankClerk: bankClerk)"," let _ = BankViewController(bank: bank) \/\/ ","...","}"],"syntax":"swift","type":"codeListing"}]},{"content":[{"inlineContent":[{"code":"이유","type":"codeVoice"},{"type":"text","text":" 그런데 weak키워드를 붙여주니 해당 타입에서 출력해주었던 메소드가 실행되지 않았다. init과 deinit을 통해 디버깅을 해보니 와일드카드 패턴으로 생성한 인스턴스가 생성과 동시에 해제되는 것을 확인할 수 있었다. "},{"type":"codeVoice","code":"와일드카드 패턴"},{"type":"text","text":"은 값을 해체하거나 무시하는 패턴중 하나이므로 "},{"type":"codeVoice","code":"weak 키워드"},{"text":"가 추가됨과 동시에 retain count가 올라가지 않기 때문에 생성과 동시에 해제되는 것이였다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"해결","type":"codeVoice"},{"type":"text","text":" 사용하지 않는 viewController(은행원, 은행)를 상수에 담으면 xcode에서 경고 메세지가 출력된다. 와일드 카드 패턴을 사용해서 순환참조 문제를 해결할 수 있다고 판단되어 weak 키워드를 제거하고 와일드카드 패턴을 사용하기로 결정했다."}]},{"type":"codeListing","syntax":"swift","code":["final class Bank {"," private let bankClerk: BankClerk"," private var customerQueue = Queue()"," var delegate: BankDelegate?","...","}","","func run() {"," let bankClerk = BankClerk()"," let bank = Bank(bankClerk: bankClerk)"," let bankManager = BankManager(bank: bank)"," let _ = BankClerkViewController(bankClerk: bankClerk)"," let _ = BankViewController(bank: bank) ","...","}"]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[Escaping Closure]","type":"text"}]}],"type":"paragraph"},{"code":["class ViewModel {"," var completionhandler: (() -> Void)? = nil"," "," func fetchData(completion: @escaping () -> Void) {"," completionhandler = completion"," }","}"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"inlineContent":[{"text":"Escaping 클로저란?","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"함수 밖(Escaping)에서 실행되는 클로저"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"즉 함수 안에서 정의된 클로저가 외부 변수들에 대한 접근을 허용할 때 사용한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"사용되는 흔한 예로는 비동기로 실행되는 HTTP Request CompletionHandler가 있다."}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"codeListing","code":["func makeRequest(_ completion: @escaping (Result<(Data, URLResponse), Error>) -> Void) {"," URLSession.shared.dataTask(with: URL(string: \"http:\/\/jusung.github.io\/\")!) { data, response, error in"," if let error = error {"," completion(.failure(error))"," } else if let data = data, let response = response {"," completion(.success((data, response)))"," }"," }","}"],"syntax":"swift"},{"content":[{"inlineContent":[{"code":"makeRequest()","type":"codeVoice"},{"text":" 함수에서 사용되는 ","type":"text"},{"type":"codeVoice","code":"completion"},{"type":"text","text":" 클로저는 함수 실행 중에 즉시 실행되지 않고 URL 요청이 끝난 후 비동기로 실행된다."},{"text":" ","type":"text"},{"type":"text","text":"이 경우에도 "},{"code":"completion","type":"codeVoice"},{"text":"의 타입에 ","type":"text"},{"code":"@escaping","type":"codeVoice"},{"type":"text","text":"을 붙여서 escaping 클로저라는 것을 명시해줘야 한다."},{"type":"text","text":" "},{"type":"text","text":"보통 클로저가 다른 변수에 저장되어 나중에 실행되거나 비동기로 실행될 때 escaping 클로저가 사용된다."}],"type":"paragraph"}],"style":"note","name":"Note","type":"aside"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"기본적으로 @escaping 키워드를 따로 명시하지 않는다면 매개변수로 사용되는 클로저는 기본으로 비탈출 클로저이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"클로저가 함수를 탈출할 수 있는 경우"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"함수 외부에 정의된 변수나 상수에 저장되어 함수가 종료된 후 사용할 경우"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"예를 들어 비동기로 작업을 하기 위해서 컴플리션 핸들러를 전달인자를 이용해 클로저 형태로 받는 함수","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"함수가 작업을 종료하고 난 이후 (즉, 함수의 return 후)에 컴플리션 핸들러, 즉 클로저를 호출하기 때문에 클로저는 함수를 탈출해 있어야만 한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"함수의 전달일자로 전달받은 클로저를 다시 반환할 때도 마찬가지이다."}],"type":"paragraph"}]}]}]}]}]},{"content":[{"inlineContent":[{"inlineContent":[{"type":"text","text":"escaping 클로저임을 명시한 경우"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"클로저 내부에서 해당 타입의 프로퍼티나, 메소드, 서브스크립트 등에 접근하려면 self 키워드를 명시적으로 사용해야 한다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"escaping 클로저를 사용할 때는 접근할 수 있는 경우의 수가 많기 때문에 어디에 접근하는 것인지 명확하게 해줘야 한다는 것이다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"비탈출 클로저는 self 키워드를 꼭 써주지 않아도 된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"class와 같은 참조 타입이 아닌 Struct, enum과 같은 값타입에서는 mutating reference의 캡쳐를 허용하지 않기 때문에 self 사용이 불가능 하다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[Auto Closure]","type":"text"}]}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"함수의 전달인자로 전달하는 표현을 자동으로 변환해주는 클로저"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"자동 클로저는 전달인자를 갖지 않는다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"호출되었을 때 자신이 감싸고 있는 코드의 결괏값을 반호나한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Swift에는 "},{"code":"함수 타입","type":"codeVoice"},{"text":" 이라는 것이 있다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉 어떤 상수나 변수에 함수를 저장해둘 수 있다는 말이다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"자동클로저는 클로저가 호출되기 전까지 클로저 내부의 코드가 동작하지 않는다. 따라서 연산을 지연시킬 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 과정은 연산에 자원을 많이 소모한다거나 부작용이 우려될 때 유용하게 사용할 수 있다."}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"왜냐하면 코드의 실행을 제어하기 좋기 때문이다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"codeListing","code":["var customersInLine: [String] = [\"ari\", \"yong\", \"han\", \"hami\"]","","\/\/ 클로저를 만들어두면 클로저 내부의 코드를 미리 실행하지 않고 가지고만 있는다.","let customerProvider: () -> String = {"," return customersInLine.removeFirst()","}","","func serveCustomer(_ customerProvider: @autoclosure () -> String) {"," print(customerProvider())","}","","serveCustomer(customerProvider()) \/\/ ari"],"syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"String 값을 반환하는 함수를 매개변수로 받는다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"여기서 auto closure를 사용하게 되면 함수 타입의 반환타입이 매개변수의 타입이 된다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"String 타입의 값을 전달받는 이유는 자동 클로저의 반환타입이 String이기 때문이다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"자동 클로저는 전달인자를 갖지 않기 때문에 반환타입의 값이 자동클로저의 매개변수로 전달되면 이를 클로저로 바꿔줄 수 있는 것이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 자동클로저를 사용하면 기존의 사용방법처럼 클로저를 전달인자로 넘겨줄 수 없다.","type":"text"}]}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/icksw.tistory.com\/157"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/jusung.github.io\/Escaping-Closure\/","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스위프트 프로그래밍(3판) 264p"}]}]}]}]}]}]}],"schemaVersion":{"patch":0,"minor":3,"major":0},"kind":"article","sections":[],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://google.github.io/swift/#delegate-methods":{"identifier":"https:\/\/google.github.io\/swift\/#delegate-methods","titleInlineContent":[{"type":"text","text":"Swift Style Guide"}],"url":"https:\/\/google.github.io\/swift\/#delegate-methods","type":"link","title":"Swift Style Guide"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211226_associated_type,_responder_chain,_touch_event,_run_loop.json b/docs/data/documentation/arinote/211226_associated_type,_responder_chain,_touch_event,_run_loop.json new file mode 100644 index 0000000..bd92365 --- /dev/null +++ b/docs/data/documentation/arinote/211226_associated_type,_responder_chain,_touch_event,_run_loop.json @@ -0,0 +1 @@ +{"variants":[{"paths":["\/documentation\/arinote\/211226_associated_type,_responder_chain,_touch_event,_run_loop"],"traits":[{"interfaceLanguage":"swift"}]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211226_associated_type,_Responder_Chain,_Touch_Event,_Run_Loop","interfaceLanguage":"swift"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"metadata":{"roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}],"title":"211226 associated type, Responder Chain, Touch Event, Run Loop"},"primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","level":1,"type":"heading"},{"inlineContent":[{"type":"text","text":"12월 26일 (일)"}],"type":"paragraph"},{"text":"학습 내용","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"associated type","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Responder Chain"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Touch Event","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Run Loop"}]}]}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"type":"heading","text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[associated type]"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로토콜에서 사용되는 제네릭 Placeholder의 느낌이다."}]}]},{"content":[{"inlineContent":[{"text":"associated type은 원래 typealias였는데, Swift 2.2부터 Associated Type이란 키워드로 바뀌었다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"syntax":"swift","code":["protocol Test {"," associatedtype MyType"," var name: MyType { get }","}"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"만약 정의된 프로퍼티가 String이나 다른 타입이 될 수 있는 여지가 있다면 위 예제처럼 associatedtype 키워드를 사용하면 된다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로토콜을 채택하여 구현할 시에는 MyType이 아니라 String, Int 등등 원하는 타입으로 정의하여 구현해줄 수 있게 된다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"associated type은 제약을 줄 수도 있다."}]}]}]},{"type":"codeListing","syntax":"swift","code":["protocol Test {"," associatedtype MyType: Equatable"," var name: MyType { get }","}"]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"이렇게 제약사항을 걸어주면 name이 무슨 타입인진 모르겠지만 Equatable을 준수하는 타입이어야 한다고 제약사항을 걸어주게 되는 것이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 Associated Type은 진짜 타입을 주는 것이 아니라 타입의 견본을 주는 것이라고 볼 수 있다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"사용할 실제 타입은 프로토콜이 적용될 때 까지 지정되지 않으니 유용하게 쓸 수 있다."}],"type":"paragraph"}]}]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Using Responders and the Responder Chain to Handle Events]"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"앱은 responder 객체를 이용하여 이벤트를 핸들링한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"responder 객체는 UIResponder클래스의 인스턴스이고, 공통적으로 UIView, UIViewController, UIApplication 객체를 서브클래스 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Responder는 이벤트 데이터를 받거나 처리하고, 아니면 다른 Responder 객체로(해당 이벤트를 해결할 수 있는)전달해야 한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱이 이벤트를 받으면 UIKit은 가장 적절한 first responder에게 이벤트를 보낸다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"처리되지 않은(Unhandled)이벤트는 responder chain에 의해서 resopnder객체로 전달된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/7FTLl6M.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"만약 텍스트필드가 이벤트를 핸들링하지 않으면, UIKit은 이벤트를 상위 UIView로 보낸다."}]}]},{"content":[{"inlineContent":[{"text":"윈도우까지 이벤트가 전달됐음에도 이벤트를 핸들링할 수 없으면 이벤트는 UIApplication까지 전달되며, and possibly to the app delegate if that object is an instance of UIResponder and not already part of the responder chain….??","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[이벤트의 first responder 결정하기]","type":"text"}]}]},{"type":"aside","name":"Note","content":[{"inlineContent":[{"text":"UIKit은 이벤트의 타입에 따라서 FirstResponder 객체를 지정한다.","type":"text"}],"type":"paragraph"}],"style":"note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"Touch event"}]},{"type":"text","text":" 터치가 일어난 뷰"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"Press event","type":"text"}],"type":"strong"},{"type":"text","text":" 초점을 갖는 객체"}]}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"text":"Shake-motin event","type":"text"}]},{"type":"text","text":" 직접 지정했거나 UIKit이 지정한 객체"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"Remote-Control event"}],"type":"strong"},{"type":"text","text":" 직접 지정했거나 UIKit이 지정한 객체"}]}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"Editing menu messages"}]},{"type":"text","text":" 직접 지정했거나 UIKit이 지정한 객체"}],"type":"paragraph"}]}]},{"type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Note"},{"type":"text","text":" "},{"type":"text","text":"가속 움직임, 자이로스콥, 자력 탐지와 같은 모션 이벤트는 리스폰더를 따르지 않는 대신 코어모션이 지정된 객체에 이런 이벤트를 전달한다."}]}],"name":"Note","style":"note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"컨트롤은 액션 메세지를 사용해 직접 관련이 있는 목표 객체와 소통한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"사용자 컨트롤과 상호작용하면 컨트롤은 목표 객체에 액션메세지를 보낸다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"액션 메세지는 이벤트가 아니다.그러나 여전히 Responder chain을 이용한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컨트롤러 목표 객체가 "},{"code":"nil","type":"codeVoice"},{"type":"text","text":"일 때 "},{"type":"codeVoice","code":"UIKit"},{"type":"text","text":"은 목표 객체로 부터 시작해 메뉴는 "},{"type":"codeVoice","code":"cut(_:)"},{"type":"text","text":", "},{"type":"codeVoice","code":"copy(_:)"},{"text":", ","type":"text"},{"type":"codeVoice","code":"paste(_:)"},{"type":"text","text":"와 같은 이름을 가진 메소드를 구현한 Responder 객체를 탐색하기 위해 앞서 설명한 동작을 사용한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"GestureRecognizer는 터치와 프레스 이벤트를 뷰가 받기전에 받는다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"만약 뷰의 "},{"code":"GestureRecognizer","type":"codeVoice"},{"text":"가 터치의 연속을 인식하는 것에 실패한다면, ","type":"text"},{"type":"codeVoice","code":"UIKit"},{"type":"text","text":"은 뷰에 터치를 보낸다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"만약 뷰가 터치를 처리하지 않는다면 ","type":"text"},{"type":"codeVoice","code":"UIKit"},{"type":"text","text":"은 터치를 "},{"code":"Responder chain","type":"codeVoice"},{"type":"text","text":"으로 전달한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"GestureRecognizer의 이벤트 처리와 관련한 내용은 ","type":"text"},{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/touches_presses_and_gestures\/handling_uikit_gestures","isActive":true,"type":"reference"},{"type":"text","text":"를 살펴보기 바란다."}]}]}]},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[어떤 Responder가 터치 이벤트를 가져야하는지 결정하기]","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"UIKit은 터치 이벤트가 어디에서 발생했는지 결정하기 위해 view-based hit testing을 사용한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"구체적으로 UIKit은 뷰 계층구조에서 뷰 객체의 bounds에 터치 위치를 비교한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UIView의 ","type":"text"},{"code":"hitTest(_:with:)","type":"codeVoice"},{"type":"text","text":" 메소드는 뷰 계층구조를 따라 터치이벤트에 대한 First Responder가 될 수 있는 특정 터치를 포함한 가장 깊은 하위뷰를 탐색한다."}]}]}]},{"style":"note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Note"},{"type":"text","text":" "},{"type":"text","text":"만약 터치 위치가 뷰의 bounds 밖에 있다면, "},{"type":"codeVoice","code":"hitTest(_:with:)"},{"type":"text","text":" 메소드는 해당 뷰와 그 뷰의 모든 하위뷰를 무시한다. 결과적으로 뷰의 "},{"code":"clipsToBounds","type":"codeVoice"},{"text":" 속성이 ","type":"text"},{"type":"codeVoice","code":"false"},{"type":"text","text":"일 경우 뷰 bounds의 하위뷰 밖은 터치가 발생했다고 하더라도 반환되지 않는다. hit-testing 동작에 대한 더 많은 정보는 "},{"type":"codeVoice","code":"UIView"},{"type":"text","text":"에서 "},{"isActive":true,"type":"reference","identifier":"https:\/\/velog.io\/@panther222128\/Responder-Chain#:~:text=https%3A\/\/developer.apple.com\/documentation\/uikit\/uiview\/1622469%2Dhittest"},{"text":" 메소드를 살펴보기 바란다.","type":"text"}]}],"name":"Note","type":"aside"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"터치가 발생하면 ","type":"text"},{"type":"codeVoice","code":"UIKit"},{"text":"은 ","type":"text"},{"code":"UITouch","type":"codeVoice"},{"text":"객체를 생성하고 이를 뷰에 연관시킨다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"터치 위치 혹은 다른 파라미터가 변경되면 "},{"type":"codeVoice","code":"UIKit"},{"text":" 같은 ","type":"text"},{"type":"codeVoice","code":"UITouch"},{"text":" 객체에 새로운 정보를 업데이트 한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"변경되지 않는 속성은 오직 View이다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"터치 위치가 기존 뷰 밖으로 이동하더라도 터치의 뷰 속성에서 값은 변경되지 않는다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"터치가 끝나면 "},{"code":"UIKit","type":"codeVoice"},{"text":"은 ","type":"text"},{"type":"codeVoice","code":"UITouch"},{"text":" 객체를 해제한다.","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[Responder Chain을 바꾸기]"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Responder 객체의 다음 속성을 override하므로써 Responder Chain을 변경할 수 있다. 이렇게 할 경우 다음 Responder는 반환한 객체가 된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"많은 "},{"type":"codeVoice","code":"UIKit"},{"type":"text","text":" 클래스가 이미 이 속성을 override하고 있으며 특정 객체를 반환한다. 아래 내용을 포함한다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"UIKit","type":"codeVoice"},{"text":" 객체. 만약 View가 ViewController의 root View이면, 다음 Responder는 ViewController이다. 그렇지 않다면 다음 Responder는 뷰의 Super view이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"UIViewController"},{"text":" 객체","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"만약 뷰 컨트롤러의 뷰가 윈도우의 root View이면 다음 Responder는 Window 객체다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"만약 ViewController가 다른 뷰 컨트롤러에 의해 제시되었었다면, 다음 Responder는 해당 뷰 컨트롤러를 제시한 ViewController이다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"UIWindow"},{"text":" 객체. 윈도우의 다음 Responder는 ","type":"text"},{"code":"UIApplication","type":"codeVoice"},{"type":"text","text":" 객체다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"UIApplication","type":"codeVoice"},{"text":" 객체. 다음 리스폰더는 AppDelegate이다. AppDelegate가 ","type":"text"},{"type":"codeVoice","code":"UIResponder"},{"text":"의 인스턴스일 때에만 그렇다. View, ViewController, App 객체 자체일 때는 그렇지 않다.","type":"text"}]}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[Touches, Presses, and Gestures]","type":"text"}]}]},{"name":"Note","type":"aside","style":"note","content":[{"inlineContent":[{"type":"text","text":"Gesture recognizers에 있는 앱의 이벤트 처리 로직을 캡슐화하므로써 앱에서 해당 코드를 재사용할 수 있게 한다."}],"type":"paragraph"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"표준 ","type":"text"},{"code":"UIKit","type":"codeVoice"},{"type":"text","text":" 뷰와 컨트롤을 사용해 앱을 빌드하고 있다면, "},{"type":"codeVoice","code":"UIKit"},{"type":"text","text":"은 자동으로 터치 이벤트(멀티 터치 포함)을 자동으로 처리한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그러나 컨텐츠를 보여주기 위한 커스텀 뷰를 사용하는 경우 뷰에서 발생하는 터치 이벤트 모두는 직접 처리해야한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"터치이벤트를 직접 처리하는 방법은 두가지가 있다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"터치를 추적하기 위해 gesture recognizers를 사용한다. Handling UIKit Gestures를 참고바란다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"code":"UIView","type":"codeVoice"},{"text":" 서브 클래스에서 직접 터치를 추적한다 Handling Touches in Your View를 참고바란다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[Handling UIKit Gestures]"}],"type":"strong"}]},{"content":[{"inlineContent":[{"text":"Gestrue recognizer를 사용하는 것은 뷰에서 발생하는 Touch나 Press 이벤트를 다룰 수 있는 가장 간단한 방법이다. 어떤 뷰든간에 한개 혹은 여러개의 Gesture에 대한 Recognizer를 붙일 수 있다. Gestrue recognizer는 뷰 위에서 발생하는 일련의 패턴이 존재하는 이벤트들 (Double-Tap, Swipe, Pinch 등등)을 처리하기 위해 Target-Action 패턴을 사용하고 이벤트가 발생하면 Target객체에 이러한 사실을 전달하여 해당 이벤트를 처리할 수 있는 액션 메소드를 호출한다.","type":"text"}],"type":"paragraph"}],"name":"Note","type":"aside","style":"note"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/TFTEfOX.png"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Gestrue Recognizer에는 두 종류가 있다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"불연속 Gestrue Recognizer"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이벤트를 인식한 후 액션 메소드를 한번만 호출"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UITapGestrueRecognizer"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"연속 Gestrue Recognizer"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"최초 이벤트 인식 후 이벤트의 변화를 추적하며 액션 메소드를 변화에 맞춰 호출"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIPanGestureRecognizer"}]}]}]}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[Configuring a Gesture recognizer]"}],"type":"strong"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Gesture recognizer 구성 단계"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"스토리 보드에서 Gesture Recognizer를 뷰 위에 드래그하여 올려놓는다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"액션 메소드를 구현한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"액션 메소드와 Gesture Recognizer를 연결한다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"text":"코드로 이를 구현할 때는 ","type":"text"},{"code":"addGestureRecognizer(_:)","type":"codeVoice"},{"type":"text","text":"를 사용한다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[Responding to Gestures]","type":"text"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"액션 메소드를 통해 Gesture를 적절히 처리해준다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"위에서 언급했듯 불연속적 Gesture는 버튼과 같이 한번의 Gesture에 대해 액션 메소드는 한번만 호출된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"연속적 Gesture에 대해서는 이벤트를 추적하고 이에 맞게 액션 메소드 여러번 호출된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"UIGestureRecognizer"},{"type":"text","text":"에는 "},{"type":"codeVoice","code":"state"},{"type":"text","text":"라는 프로퍼티가 있고 이를 활용하여 액션 메소드를 구성할 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"연속 Gesture Recognizer에선 ","type":"text"},{"type":"codeVoice","code":".began"},{"text":", ","type":"text"},{"type":"codeVoice","code":".changed"},{"text":", ","type":"text"},{"type":"codeVoice","code":".ended"},{"type":"text","text":", "},{"type":"codeVoice","code":".cancelled"},{"text":"를 사용할 수 있다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"예를 들어 ","type":"text"},{"type":"codeVoice","code":".changed"},{"text":"상태에선 뷰의 속성을 임의로 변경시키고, ","type":"text"},{"type":"codeVoice","code":".ended"},{"text":"에서는 이를 확정 짓는 등의 행위를 할 수 있다.","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[Run Loop]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Global Thread에선 Timer가 동작하지 않는 이유는 뭘까?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 답을 찾기 위해 Run Loop에 대해서 공부해보자."}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Run Loop가 뭘까?"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Run Loop 객체는 소켓, 파일, 키보드 마우스 등의 입력소스를 처리하는 이벤트 처리 루프다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"쓰레드가 일해야 할 때는 일하고 일이 없으면 쉬도록 하는 목적으로 고안되었다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Run Loop 입장에서 Timer는 입력이 아닌 특수한 유형이지만 Timer의 이벤트 또한 처리한다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"요약 하자면 Run Loop는 입력 소스를 처리하는 ","type":"text"},{"type":"codeVoice","code":"이벤트 처리 루프"},{"type":"text","text":"이고, "},{"code":"Timer","type":"codeVoice"},{"text":" 또한 같이 처리한다는 것 같다.","type":"text"}]}]}]},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[Thread와 Run Loop]","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Run Loop는 어디에 사용될까?","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Thread의 외부 입력 소스 및 Timer를 처리할 때 사용된다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"중요 핵심 단어 Thread"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Thread는 모두 각자의 Run Loop를 가질 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Thread를 생성할 때 Run Loop가 자동으로 생성된다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"다만 Run Loop는 자동으로 실행되지 않는다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"자동으로 생성 되는 것은 맞지만 자동으로 실행되진 않는다."}]}]},{"content":[{"inlineContent":[{"text":"Run Loop 실행에 대한 관리는 프로그래머의 몫이다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Main Run Loop는 예외이다."}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/OFvM1l2.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Main Thread는 애플리케이션이 실행될 때 프레임워크 차원에서 자동으로 RunLoop를 설정하고 실행한다. 이를 Main RunLoop라고 한다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 Thread를 생성했는데 이 Thread가 입력소스나 Timer를 처리해야 한다면, "},{"code":"Run Loop를 직접 얻어서 실행","type":"codeVoice"},{"text":"시켜 주어야 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"내가 생성한 Thread에 대한 Run Loop를 생성해서 얻으려면 다음 메소드를 이용한다."}]}]}]},{"syntax":"swift","type":"codeListing","code":["class var current: RunLoop { get }"]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"현재 실행중인 쓰레드 내에서 다음과 같이 작성하면 현재 Thread에 대한 RunLoop를 얻을 수 있다."}]}]}]},{"code":["let runLoop = RunLoop.current"],"type":"codeListing","syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"그러나 내가 RunLoop를 얻는 것 만으로는 입력 소스 및 타이머를 처리해주진 않는다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Run이라는 것을 통해 RunLoop를 직접 실행 시켜주어야 하는데, 이것에 대해 알기 위해 Run Loop가 어떻게 작동하는지 알아보자","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[RunLoop의 작동 원리]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"RunLoop는 루프 수행할 때 총 2가지 Event Source를 수신한다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Input Source","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"다른 Thread나 Application으로부터 온 비동기 이벤트를 전달한다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Timer"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"예약된 시간 또는 반복 간격으로 발생하는 동기 이벤트를 전달한다.","type":"text"}]}]}],"type":"unorderedList"}]}]}]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/vfgJdur.png","type":"image"}],"type":"paragraph"},{"type":"aside","name":"Note","style":"note","content":[{"inlineContent":[{"type":"strong","inlineContent":[{"text":"Input Source","type":"text"}]},{"text":" 핸들러에 비동기 이벤트를 전달하고 runUntilDate 메소드가 종료","type":"text"},{"text":" ","type":"text"},{"type":"strong","inlineContent":[{"type":"text","text":"Timer Source"}]},{"text":" 핸들러에 이벤트를 전달하지만 런 루프를 종료하진 않음","type":"text"},{"text":" ","type":"text"},{"text":"런 루프는 동작에 대해 노티피케이션을 생성","type":"text"},{"text":" ","type":"text"},{"text":"등록된 런 루프 옵저버를 통해 알림을 수신하고 추가 처리를 위해 스레드에 구현하는 것이 가능","type":"text"}],"type":"paragraph"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"왼쪽 그림에서 노란색 루프를 한바퀴 도는 작업이 한번의 실행이라고 생각했을 때,","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"RunLoop는 한번의 실행 동안 내 Thread에 도착한 이벤트를 받고, 이에 대한 핸들러를 수행하는 객체","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"루프라고 해서 RubnLoop가 자체적으로 계속 이벤트가 들어오나 안들어오나 실행을 반복 한다고 생각할 수 있겠지만, RunLoop는 내부적으로 반복 실행을 하지 않는다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"한번 Event Source를 읽고 전달하는 실행이 끝나면, 그대로 대기한다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그 다음 Event Source가 들어와도 RunLoop는 대기 상태이기 때문에 Event를 받을 수 없는 것이다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"따라서 Thread 내에서 프로그래머가 명시적으로 for, while 등을 이용해 RunLoop를 반복 실행 시켜주어야 한다.","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[RunLoop를 실행시키는 방법]"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"아까 Global Thread에서 왜 Timer가 실행되지 않은지에 대해 원인을 정리해보자면,"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Global Thread를 손수 생성했고","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"내가 생성한 Global Thread의 RunLoop는 실행되고 있지 않기 때문에,"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Timer를 작동 시켰지만, 내 Thread의 RunLoop가 이 Event를 처리하지 못해서 실행이 안됐던 것이였다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"따라서 Global Thread에서 RunLoop를 실행 시키는 방법은 아래 메소드를 활용하는 것이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/jOKErSA.png","type":"image"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Loop를 Running 시키는 방법 중 4가지 메소드를 알아보자","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"run()"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"receiver를 영구 루프에 넣고, 이 기간 동안 모든 부착된 Input Sourcer의 데이터를 처리한다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"run(mode:before:)"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"루프를 한 번 실행하여 지정된 모드에서 지정된 날짜까지 input을 blocking한다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"RunLoop Mode에 대해서 참고해보자."}]}]}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"code":"run(until:)","type":"codeVoice"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"지정된 날짜까지 루프를 실행하고, 그 기간 동안 루프는 부착된 모든 Inptu Source들의 데이터를 처리한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"보통 RunLoop를 반복 실행할 때 이 메소드를 사용한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"만약 더이상 RunLoop가 필요없어지면 아래 코드 예제의 while문을 false로 해주어 RunLoop가 더이상 작동하지 않도록 설정하면 된다."}]}]}],"type":"unorderedList"},{"syntax":"swift","type":"codeListing","code":["while isRunning {"," runLoop.run(until: Date().addingTimeInterval(0.1))","}"]}]},{"content":[{"inlineContent":[{"code":"acceptInput(forMode:before:)","type":"codeVoice"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"지정된 날짜까지 또는 지정된 모드에 대해서만 입력을 허용하여 루프를 한번만 실행한다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[언제 RunLoop를 사용할까?]","type":"text"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Input Source를 통해 다른 Thread와 통신하는 경우","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Timer를 사용해야 하는 경우"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Perform Selector Source를 사용해야 하는 경우"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"주기적인 일을 계속 수행해야 하는 경우","type":"text"}]}]},{"content":[]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/taekki-dev.tistory.com\/34"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/zeddios.tistory.com\/382"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/seizze.github.io\/2019\/11\/26\/iOS%EC%9D%98-Responder%EC%99%80-Responder-Chain-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0.html"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/touches_presses_and_gestures\/using_responders_and_the_responder_chain_to_handle_events","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/velog.io\/@panther222128\/Touches-Presses-and-Gestures#:~:text=0-,https%3A\/\/developer.apple.com\/documentation\/uikit\/touches_presses_and_gestures,-%22Encapsulate%20your%20app%27s","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/baked-corn.tistory.com\/130","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/touches_presses_and_gestures\/implementing_a_custom_gesture_recognizer\/implementing_a_continuous_gesture_recognizer?changes=_4","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/library\/archive\/documentation\/Cocoa\/Conceptual\/Multithreading\/RunLoopManagement\/RunLoopManagement.html"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/babbab2.tistory.com\/68"}],"type":"paragraph"}]}]}]}]}]}],"schemaVersion":{"patch":0,"major":0,"minor":3},"kind":"article","sections":[],"references":{"https://i.imgur.com/OFvM1l2.png":{"identifier":"https:\/\/i.imgur.com\/OFvM1l2.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/OFvM1l2.png"}],"type":"image"},"https://i.imgur.com/TFTEfOX.png":{"identifier":"https:\/\/i.imgur.com\/TFTEfOX.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/TFTEfOX.png"}],"type":"image"},"https://i.imgur.com/vfgJdur.png":{"identifier":"https:\/\/i.imgur.com\/vfgJdur.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/vfgJdur.png"}],"type":"image"},"https://i.imgur.com/7FTLl6M.png":{"identifier":"https:\/\/i.imgur.com\/7FTLl6M.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/7FTLl6M.png"}],"type":"image"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/jOKErSA.png":{"identifier":"https:\/\/i.imgur.com\/jOKErSA.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/jOKErSA.png"}],"type":"image"},"https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/handling_uikit_gestures":{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/touches_presses_and_gestures\/handling_uikit_gestures","titleInlineContent":[{"type":"text","text":"Handling UIKit Gestures"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/touches_presses_and_gestures\/handling_uikit_gestures","type":"link","title":"Handling UIKit Gestures"},"https://velog.io/@panther222128/Responder-Chain#:~:text=https%3A//developer.apple.com/documentation/uikit/uiview/1622469%2Dhittest":{"identifier":"https:\/\/velog.io\/@panther222128\/Responder-Chain#:~:text=https%3A\/\/developer.apple.com\/documentation\/uikit\/uiview\/1622469%2Dhittest","titleInlineContent":[{"type":"codeVoice","code":"hitTest(_:with:)"}],"url":"https:\/\/velog.io\/@panther222128\/Responder-Chain#:~:text=https%3A\/\/developer.apple.com\/documentation\/uikit\/uiview\/1622469%2Dhittest","type":"link","title":"hitTest(_:with:)"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211227_responder_chain,_touch_event,_dispatchsemaphore.json b/docs/data/documentation/arinote/211227_responder_chain,_touch_event,_dispatchsemaphore.json new file mode 100644 index 0000000..dcb0ca6 --- /dev/null +++ b/docs/data/documentation/arinote/211227_responder_chain,_touch_event,_dispatchsemaphore.json @@ -0,0 +1 @@ +{"kind":"article","primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","level":1,"type":"heading","text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"text":"12월 27일 (월)","type":"text"}]},{"text":"학습 내용","level":2,"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Responder Chain, Touch Event 활동학습"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"은행창구매니저 STEP2 리뷰"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"은행창구매니저 STEP3 설계과정","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"type":"heading","text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[활동학습 내용 정리]"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Shake-motin event가 발생했을 때 제스처를 처리하는 곳은 UIApplicationDelegate가 될 것 같다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Remote-Control event"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"에어팟 버튼 기능, 이어폰의 리모콘 기능","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIResponder"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Resopnder하고 handling 하기 위한 추상 클래스","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"터치 이벤트를 핸들링하기 위한 메소드가 있다."}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/zgn6a6I.png"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"실제로는 위 메소드를 UIView에서 override해서 주로 사용한다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"UIEvent","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"사용자의 인터렉션을 표현해주는 객체"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱은 상당히 다양한 종류의 이벤트를 받을 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이벤트를 받아서 처리할거냐 무시할거냐는 프로그래머의 몫"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"UITouch"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자가 터치한 위치, 사이즈, 움직임, 세기 까지 나타내는 객체"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이벤트 핸들링을 위해서 리스폰더 객체 안에 UIEvent와 함께 전달된다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"제스처는 왜 필요할까?","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"제스처 인식기에서 행동을 인식하여 일을 수행할 수 있는 방식 중 타깃-액션 방식이 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"터치이벤트를 받았다고 Responder를 하는 것은 아니다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"공식문서를 참조할 때 See Also를 통해 관련문서를 확인하자.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"하단에 ","type":"text"},{"code":"{}","type":"codeVoice"},{"text":" 표시가 되어있는 링크는 예제코드가 있다.","type":"text"}]}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"Q1 : iOS 환경에서 사용자의 터치 이벤트를 알아채거나 제어할 수 있는 방법의 종류를 알아봅시다","type":"text"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"터치 이벤트를 알아채는 객체"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"UIResponder"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"사용자의 터치 이벤트를 제어할 수 있는 방법"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"Responder Recognizer에서 행동을 인식하여 일을 수행하는 방식-> Taget-Action, Delegate","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"Q2 : iOS 환경에서 사용자가 일으킬 수 있는 이벤트의 종류는?"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uievent\/eventtype","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/uievent\/eventsubtype"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"case touches"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이벤트는 화면의 터치와 관련이 있습니다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"case motion","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"이벤트는 사용자가 기기를 흔들 때와 같이 기기의 움직임과 관련이 있습니다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"case remoteControl","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"이벤트는 원격 제어 이벤트입니다. 원격 제어 이벤트는 장치에서 멀티미디어를 제어하기 위한 목적으로 헤드셋 또는 외부 액세서리에서 수신된 명령으로 발생합니다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"case presses","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이벤트는 물리적 버튼의 누름과 관련이 있습니다."}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"inlineContent":[{"inlineContent":[{"type":"text","text":"Q3 : 뷰 위에 텍스트 필드가 있고 텍스트 필드 위에 탭 제스쳐 인식기가 있는 상황에서 각 상황에서 사용자가 텍스트 필드 위를 탭 했을 때 어떤어떤 객체가 어떤 메서드를 통해 반응하나요?"}],"type":"strong"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"aside","name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Text Field Properties"}]}],"style":"note"},{"rows":[[[{"type":"paragraph","inlineContent":[{"type":"text","text":"USER INTERACTIVE"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"CONTROL STATE"}]}]],[[{"type":"paragraph","inlineContent":[{"text":"true","type":"text"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"enabled"}]}]],[[{"inlineContent":[{"text":"false","type":"text"}],"type":"paragraph"}],[{"inlineContent":[{"type":"text","text":"enabled"}],"type":"paragraph"}]],[[{"type":"paragraph","inlineContent":[{"type":"text","text":"true"}]}],[{"inlineContent":[{"text":"disabled","type":"text"}],"type":"paragraph"}]],[[{"type":"paragraph","inlineContent":[{"type":"text","text":"false"}]}],[{"type":"paragraph","inlineContent":[{"type":"text","text":"disabled"}]}]]],"alignments":["center","center"],"header":"row","type":"table"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/LQW9zWA.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"MyField의 "},{"code":"touchesBegan(_:with:)","type":"codeVoice"},{"type":"text","text":"가 먼저 반응하고 ViewController의 "},{"type":"codeVoice","code":"didTap(_:)"},{"text":"이 반응한 후 My Field의 ","type":"text"},{"code":"touchesCancelled(_:with:)","type":"codeVoice"},{"text":"가 호출된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"MyView의 "},{"type":"codeVoice","code":"touchesBegan(_:with:)"},{"text":"가 두번 호출되고, 이후 MyView의 ","type":"text"},{"type":"codeVoice","code":"touchesEnded(_:with:)"},{"type":"text","text":"가 두번 호출된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"MyView의 "},{"type":"codeVoice","code":"touchesBegan(_:with:)"},{"type":"text","text":"가 두번 호출되고, 이후 MyView의 "},{"code":"touchesEnded(_:with:)","type":"codeVoice"},{"type":"text","text":"가 두번 호출된다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"MyView의 "},{"type":"codeVoice","code":"touchesBegan(_:with:)"},{"text":"가 두번 호출되고, 이후 MyView의 ","type":"text"},{"code":"touchesEnded(_:with:)","type":"codeVoice"},{"type":"text","text":"가 두번 호출된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"Q4 : Responder Chain과 Gesture Recognizer는 이벤트 제어에서 상호간 상관관계일까요? 별개관계일까요? 그렇게 생각한 이유는 무엇인가요?"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"상관관계도 아니고 그렇다고 별개관계라고 보기에도 애매한 존재이다.","type":"text"}]}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[DispatchSemaphore]"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Semaphore란?"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"공유자원에 대한 접근을 막아주는 것"}]}]},{"content":[{"inlineContent":[{"text":"공유자원이 뭐일지는 만들기 나름이다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"DisPatchSemaphore는 디스패치에 대한 Semaphore이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"디스패치는 Task라고 볼 수 있다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Task == Closure","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DispatchSemaphore에 value 파라미터를 2로 준다면 Race Condition이 발생할 가능성이 있는 것인가?"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"은행 창구 매니저 프로젝트로 실험해보았는데, value의 수가 적고, 접근할 공유자원의 수도 적다면 Race Condition이 발생할 수 있는 가능성이 적은 편이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"또한 공유자원에 접근을 하지 않는다면 DispatchSemaphore의 value가 1이 아니더라도 Thread-Safe하지 않은 것이지, 무조건 Race Condition이 발생한다고 볼 수는 없다. 발생 가능성이 있다고 보면 될 것 같다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"프로젝트에 써먹어보려고 했는데 오늘 삽질해본 결과 DispatchSemaphore의 value가 1이 아니라 2라면 Race Condition 발생할 수 있는 가능성이 생기는… 문제점을 발견하게 되서… 다른 방법을 찾아봐야할 것 같다.","type":"text"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[델마의 조언]"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"개발할 때 염두해야할 것"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"기능을 선택할 때에는 비슷한 기능은 무엇이 있는지 탐색하기.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"선택한 기능이 다른 기능보다 나은점은 뭐가 있는지 생각해보기","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"기능을 찾아 사용할 때 선택한 이유를 생각해보기","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Core Foundation vs Foundation"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"코어 파운데이션에 있는 기능은 Foundation에서 래핑하여 구현되어져있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"보통 앱개발을 할 때에는 Foundation의 기능 없이 개발하기에는 어려움이 있기 때문에, Core Foundation에 내장되어있는 기능보다는 Foundation에 내장되어있는 기능을 사용하는 것을 선호하는 편이다."}]}]}]}]},{"content":[{"inlineContent":[{"text":"다음 스텝을 지레짐작해서 개발하지 말자","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"그냥 요구사항에 맞춰 최소한의 기능을 빨리 만들어보자."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"시도하고 실패하고 돌아가는 과정을 반복하며 개선해나가는 방향으로 진행하자."}]}]}]}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/stackoverflow.com\/questions\/3773776\/corefoundation-vs-foundation","type":"reference","isActive":true}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/stackoverflow.com\/questions\/20582495\/core-foundation-vs-foundation-or-core-foundation-foundation","type":"reference","isActive":true}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211227_responder_chain,_touch_event,_dispatchsemaphore"]}],"sections":[],"schemaVersion":{"major":0,"patch":0,"minor":3},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211227_Responder_Chain,_Touch_Event,_DispatchSemaphore"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"211227 Responder Chain, Touch Event, DispatchSemaphore"},"references":{"https://i.imgur.com/LQW9zWA.png":{"identifier":"https:\/\/i.imgur.com\/LQW9zWA.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/LQW9zWA.png","traits":["1x","light"]}],"type":"image"},"https://stackoverflow.com/questions/20582495/core-foundation-vs-foundation-or-core-foundation-foundation":{"identifier":"https:\/\/stackoverflow.com\/questions\/20582495\/core-foundation-vs-foundation-or-core-foundation-foundation","titleInlineContent":[{"type":"text","text":"https:\/\/stackoverflow.com\/questions\/20582495\/core-foundation-vs-foundation-or-core-foundation-foundation"}],"url":"https:\/\/stackoverflow.com\/questions\/20582495\/core-foundation-vs-foundation-or-core-foundation-foundation","type":"link","title":"https:\/\/stackoverflow.com\/questions\/20582495\/core-foundation-vs-foundation-or-core-foundation-foundation"},"https://i.imgur.com/zgn6a6I.png":{"identifier":"https:\/\/i.imgur.com\/zgn6a6I.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/zgn6a6I.png"}],"type":"image"},"https://stackoverflow.com/questions/3773776/corefoundation-vs-foundation":{"identifier":"https:\/\/stackoverflow.com\/questions\/3773776\/corefoundation-vs-foundation","titleInlineContent":[{"type":"text","text":"https:\/\/stackoverflow.com\/questions\/3773776\/corefoundation-vs-foundation"}],"url":"https:\/\/stackoverflow.com\/questions\/3773776\/corefoundation-vs-foundation","type":"link","title":"https:\/\/stackoverflow.com\/questions\/3773776\/corefoundation-vs-foundation"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211228_race_condition,_dispatchsemaphore,_http.json b/docs/data/documentation/arinote/211228_race_condition,_dispatchsemaphore,_http.json new file mode 100644 index 0000000..baa154e --- /dev/null +++ b/docs/data/documentation/arinote/211228_race_condition,_dispatchsemaphore,_http.json @@ -0,0 +1 @@ +{"metadata":{"title":"211228 Race Condition, DispatchSemaphore, HTTP","modules":[{"name":"Ari's Notes"}],"role":"article","roleHeading":"Article"},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211228_Race_Condition,_DispatchSemaphore,_HTTP"},"primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","level":1,"type":"heading"},{"inlineContent":[{"text":"12월 28일 (화)","type":"text"}],"type":"paragraph"},{"level":2,"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"은행창구매니저 STEP3 PR 작성","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"은행창구매니저 STEP4 설계","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Race Condition 발생을 방지하기 위한 다양한 방법"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"HTTP가 뭐지?"}],"type":"paragraph"}]}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"type":"heading","level":2,"text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[Race Condition 발생 방지하기]"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"애플사이다","type":"codeVoice"},{"text":", ","type":"text"},{"code":"july","type":"codeVoice"},{"type":"text","text":", "},{"code":"namu","type":"codeVoice"},{"type":"text","text":"가 프로젝트 코드를 보고 야곰닷넷 카드뽑기와 유사하면서도 간단한 방법으로 은행을 돌리고 있는데, 해당 코드가 왜 Race Condition이 발생하지 않는 것인지 질문해주었다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"나도 이 부분에 대해서 깊이 고민해보지 않았기 때문에(잘 돌아가서…) 제대로 설명할 수 없었고, 또 다시 삽질의 현장으로…..","type":"text"}],"type":"paragraph"}]}]}]}]},{"syntax":null,"code":["📄 테스트 환경","","- 아리, 허황의 스텝3 코드","- banking 의 speed 0초","- setUpBankCustomers의 range 100...100","- loanBankersCount 10명, depositBankersCount 20명"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"은행원의 수만큼 DispatchQueue를 만들어주고 있고, 그 안에서 dequeue를 하기 위해 고객의 큐에 접근하고 있다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"여러 스레드에서 고객의 큐를 접근할 수 있는 가능성이 있다."}]}]},{"content":[{"inlineContent":[{"text":"그러나 현재 우리 코드에서는 race condition없이 잘 작동한다. 이유가 무엇일까?","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"일단 은행원의 수(DispatchQueue)가 적다. 3개뿐이다. 따라서 스레드는 3개 이상 생기지 않는다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 각 업무를 할때 스레드를 잠재운다. 딜레이가 있다."}]}]},{"content":[{"inlineContent":[{"text":"고객의 숫자도 적은편이다.","type":"text"}],"type":"paragraph"}]}],"type":"orderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 결론은 현재 셋팅("},{"type":"codeVoice","code":"은행원 수"},{"type":"text","text":", "},{"type":"codeVoice","code":"고객 수"},{"text":", ","type":"text"},{"type":"codeVoice","code":"딜레이"},{"text":") 때문에 race condition이 발생하지 않는다는 판단이 들었다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"현재는 race condtion이 발생하지 않고 정상적으로 작동하지만, 결국에 ","type":"text"},{"type":"codeVoice","code":"Queue"},{"text":"는 ","type":"text"},{"code":"Thread-Safe","type":"codeVoice"},{"text":" 하지 않기 때문에, race condition이 발생하지 않을 것이라고도 장담할 수 없다는 결론이다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"따라서 DispatchSemaphore를 사용해서 접근할 수 있는 스레드의 수를 제한해주는 것이 안전할 것 같다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하지만 이 방법도 테스트 결과 "},{"code":"customers.dequeue","type":"codeVoice"},{"text":" 작업들이 for문으로 인해 작업이 쌓여있고, 큐의 접근을 세마포어로 제한하고 있으니, 제한하는 동안 쌓인 작업들이 제한이 풀리고 실행되면 비어있는 큐에 접근할 수 있기 때문에 fatalError가 발생할 수 있다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"현재 global()로 동시성큐를 사용하고 있는데, 이 부분을 Serial Queue로 만들어서 질서를 지키게 해주면 매우 안전할 것 같다. 👍","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"테스트 해보았는데 race condition 발생 안한다!!!!!","type":"text"}]}]}],"type":"unorderedList"}]}]}]}],"type":"unorderedList"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"개선된 코드"}],"type":"strong"}]}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/R2jT5ox.png","type":"image"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"멤버변수로 Serial Queue를 생성해주고 DispatchQueue.global() 대신 직접 생성해준 bankerQueue를 사용하도록 개선하였다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"DispatchQueue가 지역변수가 아니라 멤버변수여야하는 이유는 뭘까?","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"DispatchQueue에 비동기로 큐를 보내고 있는 부분이 for문에 의해 4번 불린다고 했을 때, 지역변수로 DispatchQueue가 4개, 즉 각각의 작업마다 생성될테니 실질적으로 동시에 접근하는 것을 막아주지 못할 것이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 for문 내부에서 Queue를 생성하는 것이 아니라 바깥에서 생성해서, 그 해당 큐를 사용하는 방식으로 활용해야 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Semaphore를 활용할 때에도 위와 마찬가지로 멤버변수여야 한다!!!!"}]}]}],"type":"unorderedList"}]}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[Server와 통신하는 방법]"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Socket 통신","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"한번 연결에 성공하면 연결이 계속 유지된다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"임의로 끊거나 네트워크 사정 상 끊기는 것이 아니라면 말이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 연속적(실시간)으로 데이터를 받아야하는 경우인 실시간 스트리밍, 채팅 같은 서비스에 사용된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"양방향 통신이다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Client가 Server에게 데이터를 요청하지만","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"반대로 Server가 Client에게 데이터를 요청할 수도 있다."}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"HTTP 통신"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Client에서 데이터가 필요할 때마다 Server에게 요청하고 Server는 그 데이터를 응답한다. 그리고 바로 연결이 종료되는 방식이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"우리가 주로 사용하는 블로그, 사이트 페이지 또한 처음 로드될 때 HTTP 통신으로 Server로 부터 데이터를 받아와 보여주는 것이고 바로 연결이 끊겼을 것이다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이후 다른 페이지를 가거나, 화면을 새로고침 하면 또 HTTP 통신으로 Server로부터 데이터를 받아와 보여주고 바로 연결이 끊긴다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이처럼 HTTP 통신은 단방향 통신이다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Clinet만 Server에게 요청할 수 있고","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Server는 Clinet에게 요청할 수 없다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"보통 실시간으로 서비스해야하는 경우를 제외하곤 이 HTTP 통신을 사용한다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Server의 부하를 줄여준다는 장점이 있다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"계속 연결을 맺는게 아닌, 원하는 요청에 대한 데이터만 던져주고 끊기므로 Server가 다른 접속들도 원활히 처리할 수 있어진다.","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[HTTP 메세지]"}]}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Client가 Server에게 무엇인가를 요청할 때 일정한 형식을 갖춰서 텍스트 기반의 메세지로 변환해서 전달해야한다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 때 이 메세지의 형식을 바로 HTTP 메세지라고 한다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"보통 이 HTTP 메세지를 만들고 변환하는 과정은 대부분 브라우저의 몫인데, iOS 앱의 경우는 HTTP 메세지를 직접 만들어 전송해주어야 한다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"물론 잘짜여진 라이브러리를 사용해도 되지만, 개념은 알고가야 한다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"메세지 구조?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"요청 메시지와 응답 메세지로 나뉜다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이 두 메세지는 라인, 헤더, 바디 세 부분으로 구성된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/0nAswjY.png"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"라인"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기본내용인 응답\/요청 여부, 메세지 전송 방식, 상태 등이 작성된다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"매우 정형화되어 있어 무조건 한 줄로만 작성된다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"POST \/userAccount\\login HTTP\/1.1"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"가장 처음에 POST\/GET 등의 전송 메소드를 정의하며 시작한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"뒤이어 요청내용에 대한 경로가 오고,","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"마지막으로 요청 형식에 대한 버전 정보가 온다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"헤더"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"메세지 본문에 대한 메타 정보가 들어가는 부분","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"필요한 만큼 여러줄로 작성된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"길이가 유동적이므로 바디와 구분을 짓기 위해 한줄의 공백을 삽입한다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"HTTP 메세지에서 나타나는 첫 번째 한줄의 공백이 헤더와 바디를 구분하는 경계선이다.","type":"text"}]},{"code":["Host: swiftapi.rubypaper.co.kr:2029.","Content-Type: application\/json"],"syntax":null,"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Host란 Key에는 도메인 및 포트번호가 Value로 온다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Content-Type에는 Key에는 메세지 바디의 타입을 나타내는데, 우리가 사용하는 JSON은 application\/json 이라고 설정해주면 된다.","type":"text"}],"type":"paragraph"}]}]}]}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"바디"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"실제로 보내고자 하는 메세지 본문 내용이 들어가는 부분으로 길이가 유동적이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"바디에 들어가는 메세지 형식은 헤더의 Content-Type에서 설정한 Type과 일치해야 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"JSON으로 설정한 경우에는 { “name” : “Ari” } 이런식으로 들어가야한다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"라인 처음에 정의하는 전송 메소드에는 크게 2가지 전송방식이 있다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이에 따라 HTTP 메세지의 형태가 바뀐다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"POST","type":"text"}]},{"code":["POST \/userAccount\/login HTTP\/1.1 \/\/ (1) 라인","Host: swiftapi.rubypaper.co.kr:2029. \/\/ (2) 헤더","Content-Type: application\/x-www-form-urlencoded \/\/ (2) 헤더","","account=swift%40swift.com&passwd=1234&grant_type=password \/\/ (3) 바디 "],"type":"codeListing","syntax":null},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"앞서 위에서 공부한 내용이 POST 방식이다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"헤더에 Content-Type을 명시하고 바디에 Content Type에 맞는 데이터를 넣는 것","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"text":"HTTP 메세지가 달라지는 것은 POST가 아니라 바로 GET이다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"GET","type":"text"}]},{"type":"codeListing","code":["GET \/userAccount\/login?account=swift@swift.com&passwd=1234&grant_type=password HTTP\/1.1 \/\/ (1) 라인","","Host: swiftapi.rubypaper.co.kr:2029 \/\/ (2) 헤더","Content-Type: no-cache \/\/ (3) 헤더"],"syntax":null},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"라인이 엄청 길어지고 바디가 사라진다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"자세히 보면 바디가 사라졌지만 바디에 있던 내용이 라인의 경로 뒤에 물음표(?)와 함께 합쳐져 버렸다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"또한 헤더의 Content-Type도 no-cache로 바뀌었다."}]}]},{"content":[{"inlineContent":[{"text":"이처럼 GET에서는 메세지 본문을 사용하지 않고 본문의 파라미터를 모두 URL 뒤에 연결해서 전달해버린다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이렇게 연결되는 파라미터를 쿼리 스트링(Query String)이라고 한다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"본문을 사용하지 않기 때문에 헤더의 Content-Type이 사용되지 않는다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"URL 뒤에 쿼리 스트링만 연결하면 되기 때문에 간결하게 정보를 전달할 수 있는 장점이 있다."}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"하지만 URL 경로의 허용범위가 1024Byte이기 때문에 긴 값은 전송할 수 없다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 데이터를 전송할 때 보다 필요한 정보를 요청할 때 주로 사용된다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"POST를 언제 사용해야할까?"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"서버에게 동일한 요청을 여러번 전송해도 응답은 항상 다를 수 있을 때 사용한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"서버의 상태나 데이터를 변경 시킬 때"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"예시로 게시글을 쓰고 삭제하는 것은 데이터가 변경되는 것이므로 POST로 요청해야한다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"POST, PUT, DELETE가 있기도 하다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"GET은 언제 사용하지?","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"서버에게 동일한 요청을 여러번 전송하더라도 동일한 응답이 돌아와야할 때 사용한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"서버의 상태나 데이터를 변경시키지 않을 때 사용하는데,","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"조회할 때 주로 사용한다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"게시글을 조회한다던지…","type":"text"}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"inlineContent":[{"text":"서버 데이터가 변경되지 않는 것은 GET으로!","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[RESTful API]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"REST는 어떠한 네트워크 프로토콜도 아니다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"HTTP에서 데이터를 주고 받을 때, 간단히 주고 받자해서 정의된 간단한 형식의 인터페이스를 뜻한다."}]}]},{"content":[{"inlineContent":[{"text":"이러한 REST 구조를 따라 구현된 시스템을 바로 RESTful 이라고 부른다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"매우 쉽고 간단하게 구현할 수 있어서 대부분 모바일 통신은 RESTful 기반이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"RESTful 기반으로 서버에서 요청과 응답을 주고 받을 수 있도록 정의된 형식을 바로 RESTful API라고 부른다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"REST 형식이 궁금하다면?"}]},{"items":[{"content":[{"inlineContent":[{"text":"https:\/\/meetup.toast.com\/posts\/92","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 RESTful API에서 주고받는 내용들은 모두 HTTP 메세지의 본문에 담겨서 전달되는데, RESTful API는 메세지 본문을 JSON 형태로 구성해서 보낸다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 RESTful API로 통신하기 위해선 JSON 데이터 형식을 잘 알아야 한다.","type":"text"}]}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[HTTPS]"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"HTTP는 다음과 같이 암호화 되지 않은 평문으로 데이터를 전송한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/uigDXFS.png"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 누군가 패킷을 훔쳐보는 스니핑(Sniffing) 공격에 취약하다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"HTTP 통신에선 Wire Shark를 통해 패킷을 확인하면 내 주민번호가 그대로 서버로 전송되는 것을 볼 수 있다."}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"누가 패킷을 훔쳐보면 내 주민번호는 그대로 털리는 것…"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"때문에 서버와 클라이언트 간 데이터를 보호하기 위해선 패킷을 암호화 한 뒤 전송해야 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/owNyiHk.png"}]}]},{"content":[{"inlineContent":[{"text":"이것이 바로 HTTPS다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"이때 사용하는 보안인증(암호화 + 서버인증 등)이 TLS(SSL)이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"요약하자면 HTTP 통신에서 보안을 추가한 것이 HTTPS 라는 것","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고로 애플은 2016년 10월 경부터 앱스토어에 등록되는 모든 앱은 HTTPS 프로토콜을 이용한 네트워크 통신만 허용하고 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 서버와 연동하는 앱이라면 서버에서 미리 인증서를 발급받아 준비해두어야 한다."}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"오동나무 최고…👍","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/babbab2.tistory.com\/70"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]}],"schemaVersion":{"minor":3,"major":0,"patch":0},"variants":[{"paths":["\/documentation\/arinote\/211228_race_condition,_dispatchsemaphore,_http"],"traits":[{"interfaceLanguage":"swift"}]}],"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"sections":[],"references":{"https://i.imgur.com/uigDXFS.png":{"identifier":"https:\/\/i.imgur.com\/uigDXFS.png","type":"image","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/uigDXFS.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]},"https://i.imgur.com/0nAswjY.png":{"identifier":"https:\/\/i.imgur.com\/0nAswjY.png","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/0nAswjY.png"}]},"https://i.imgur.com/owNyiHk.png":{"identifier":"https:\/\/i.imgur.com\/owNyiHk.png","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/owNyiHk.png"}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/R2jT5ox.png":{"identifier":"https:\/\/i.imgur.com\/R2jT5ox.png","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/R2jT5ox.png"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211229_tcp_ip.json b/docs/data/documentation/arinote/211229_tcp_ip.json new file mode 100644 index 0000000..2e4ae0d --- /dev/null +++ b/docs/data/documentation/arinote/211229_tcp_ip.json @@ -0,0 +1 @@ +{"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211229_TCP_IP","interfaceLanguage":"swift"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211229_tcp_ip"]}],"primaryContentSections":[{"kind":"content","content":[{"type":"heading","text":"TIL (Today I Learned)","level":1,"anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"type":"text","text":"12월 29일 (수)"}]},{"type":"heading","text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"TCP\/IP 예습","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"text":"고민한 점 \/ 해결 방법","type":"heading","level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[TCP\/IP]"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"인터넷이란?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"전 세계에 걸쳐 파일 전송 등의 데이터 통신 서비스를 받을 수 있는 컴퓨터 네트워크의 시스템","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"인터넷을 비용을 주고 사용한다"}]},{"items":[{"content":[{"inlineContent":[{"text":"이것은 사업자가 만들어 놓은 네트워크 인프라를 사용하는 것","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"TCP\/IP란?","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"인터넷에서 컴퓨터들이 서로 정보를 주고 받는데 쓰이는 프로토콜의 집합"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"계층"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"Application Layer","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"특정 서비스를 제공하기 위해 애플리케이션끼리 정보를 주고 받을 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"브라우저와 웹서버가 HTTP 요청, 응답을 통해 통신하는 것을 예로 들 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"HTTP, FTP, SSH, Telnet, DNS, SMTP 등과 같은 프로토콜이 사용된다","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Transport Layer"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"송신된 데이터를 수신측 애플리케이션에 확실히 전달하게 해준다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"네트워크 통신을 하는 애플리케이션은 포트번호를 사용하게 된다."}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"따라서 Transport Layer는 포트번호를 사용해서 애플리케이션을 찾아주는 역할을 한다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"TCP, UDP, RTP, RTCP 와 같은 프로토콜이 사용된다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Internet Layer"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"수신 측까지 데이터를 전달하기 위해 사용된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"송신측, 수신측 모두 IP주소를 가지고 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"IP주소를 바탕으로 올바른 목적지로 찾아갈 수 있도록 해준다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"IP, ARP, ICMP, RARP, OSPF 같은 프로토콜이 사용된다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Network Access Layer"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"네트워크에 직접 연결된 기기간의 데이터 전송을 도와준다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"여기서는 물리적인 주소인 MAC주소를 사용한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Ethernet, PPP, Token Ring 같은 프로토콜이 사용된다.","type":"text"}]}]}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"www.google.com을 웹브라우저에 입력하면 무슨일이 일어날까요?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"웹브라우저에 위 주소를 입력한다는 것은 구글 웹서버의 80포트로 아래와 같은 HTTP Request 메세지를 보내는 것이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/86md72e.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"해당 요청을 인터넷을 통해 구글서버로 전달하기 위해 우리는 패킷을 만들어야 한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"패킷에는 각 계층에 필요한 정보들이 담겨야 한다."}]}]},{"content":[{"inlineContent":[{"text":"그리고 여기서는 각 계층 별로 HTTP, TCP, IP, Ethernet 프로토콜을 사용한다고 가정하자.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"일단 패킷의 Application Layer에는 위에서 작성한 HTTP Request가 들어간다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/vHyWDtp.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"TCP 패킷의 헤더는 아래와 같이 생겼다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Ji52ep5.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"다른 부분이 많지만 우선 중요하게 볼 것은 SP와 DP다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"시작 포트번호와 목적지 포트번호를 말한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"시작 포트번호는 내 컴퓨터에서 만든 소켓의 포트 번호라서 당연히 내 컴퓨터는 알고 있을 것이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그리고 목적지 포트번호 또한 80으로 우리는 알고있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"80은 웹서버의 웰노운 포트번호다."}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/BQRrwPs.png","type":"image"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"다음으로는 IP 헤더에 대한 정보를 채워보자."}]}]},{"content":[{"inlineContent":[{"text":"여기서도 중요한 정보는 SA와 DA 즉, 시작 IP주소와 목적지 IP주소이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"나의 시작 IP주소는 알고있겠지만, 목적지 IP주소는 아직 모른다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"지금은 www.google.com 이라는 도메인 정보만 알고 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"그렇지만 DNS 프로토콜을 통해서 도메인 정보로 IP주소를 알아낼 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/rG7YJ10.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"브라우저는 OS에게 domain에 대한 ip주소를 알고 싶다고 요청한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그러면 OS에서 DNS서버로 요청을 보내게 된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/IAKcFA1.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그럼 OS가 DNS서버를 어떻게 알고있을까?","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"DNS 서버 주소는 이미 컴퓨터에 등록이 되어있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"맥북의 경우 시스템 환경설정 - 네트워크에 들어가보면 내가 알고있는 DNS서버 주소를 볼 수 있다."}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/dtxFR1e.png","type":"image"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DNS 또한 HTTP와 같은 애플리케이션 계층 프로토콜이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그리고 DNS는 53번 포트를 사용한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Hqb9XiF.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DNS도 HTTP Request와 비슷하게 도메인이 담긴 쿼리를 도메인 서버로 보낸다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/4ynNjrF.png"}]}]},{"content":[{"inlineContent":[{"text":"그러면 도메인 서버가 IP주소를 응답해준다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DNS는 Transport Layer에서 UDP라는 프로토콜을 사용한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/xyBWUo9.png","type":"image"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"UDP는 TCP와 다르게 헤더가 간단하다. 포트번호말고 다른게 없다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"그 이유는 UDP가 비연결지향형 프로토콜이기 때문이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"TCP는 연결지향형 프로토콜이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이제 DNS를 통해 성공적으로 도메인 이름에 대한 IP주소를 받아오게 되었다.","type":"text"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/Z375cPd.png","type":"image"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/XMqsm2L.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"마지막으로 Ethernet 프로토콜에 대한 헤더를 만들어야 하는데 아직 MAC 주소를 모른다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"MAC주소를 어떻게 알아봐야할까?"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"MAC주소를 알아오기 전에 이전까지는 우리가 목표인 구글서버에 대한 정보가 필요했다."}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/t0qsmsO.png"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"그러면 MAC 주소도 우리의 목적지인 구글 웹서버의 MAC주소가 필요할까?"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"여기서 MAC주소는 구글의 MAC 주소 대신, 물리적으로 연결된 우리집 공유기의 MAC 주소가 필요하다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/uJsN2C0.png"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이 공유기를 통해 다른 네트워크와 연결이 가능하니 게이트웨이라고 부르기도 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 게이트웨이의 IP는 이미 알고있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Netstat 명령어를 통해 확인할 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/cAGkkvb.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그럼 어떻게 IP주소로 MAC주소를 알 수 있을까?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"IP주소로 MAC주소를 알아내기 위해서 ARP 프로토콜을 사용한다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"ARP란?","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"IP주소를 MAC주소로 바꾸어주는 주소해석 프로토콜","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"자 이제 MAC 주소까지 알아냈으니 패킷이 각박한 네트워크 세계로 나갈 준비가 되었다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그런데 요청을 보내기 전에 한가지 더 봐야할게 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"바로 TCP가 연결지향형 프로토콜이라는 점이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그래서 TCP 프로토콜은 데이터를 전송하기 위해 송신측과 수신측이 서로 연결되는 작업이 필요하다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/79Nhvhh.png"}]}]},{"content":[{"inlineContent":[{"text":"이러한 작업을 3 Way Handshaking이라고 불린다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"3 Way Handshaking을 수행하기 위해서는 TCP 헤더에 표시한 플래그들이 사용된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이러한 플래그들을 컨트롤 비트라고 부른다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"3 Way Handshaking 에서는 SYN과 ACK 플래그가 사용된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/xBJ4FOx.png","type":"image"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"클라이언트는 서버에게 접속을 요청하는 SYN 패킷을 보낸다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"서버는 SYN 요청을 받고 클라이언트에게 요청을 수락한다는 ACK과 SYN 플래그가 설정된 패킷을 보낸다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"클라이언트는 서버에게 다시 ACK을 보낸다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이제부터 연결이 이루어지고 데이터가 오가게 된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이제 3 Way Handshaking으로 연결이 성립되었으니 데이터가 보내질 차례이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"내가 사용하는 컴퓨터는 Private IP를 사용하고 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/nIa3aUV.png"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Private IP는 외부의 네트워크 환경에서 IP주소를 찾지 못한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그래서 공유기를 통해 나갈 때 public ip 주소를 변환하여 나가는 작업이 필요하다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이러한 작업을 NAT(Network Adress Translation)이라고 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"우리집 공유기를 거치고 나서 구글서버에 도착하기 위해 여러 라우터를 거쳐야 한다."}]}]},{"content":[{"inlineContent":[{"text":"라우터는 네트워크와 네트워크를 연결해주는 역할을 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"라우터가 목적지 경로를 찾아나가는 과정을 라우팅이라고 한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/S5S7xjv.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"라우팅을 거쳐 구글서버가 연결된 라우터까지 데이터가 도착을 하면 패킷의 IP헤더에 기록된 구글 서버 IP주소를 통해 MAC 주소를 얻어와야 한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이때 이전에 설명했던 ARP프로토콜을 사용한다. APR는 라우터가 연결된 네트워크에 브로드 캐스팅 된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"목적지 구글서버가 자신의 IP로 온 ARP요청을 받으면 MAC주소를 응답해준다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"이제 목적지 구글서버의 MAC 주소를 알았으니 데이터가 물리적으로 전달될 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"ARP로 IP주소를 통해 MAC주소를 얻고 목적지 구글서버에 데이터가 도착했다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Internet Layer의 IP주소와 Network Access Layer의 MAC주소를 사용해서 올바른 목적지에 도착했으니 Transport Layer부터 알아보자.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Transport Layer의 목적지 포트번호에는 80번이 적혀있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이것을 보고 80번 포트를 사용하고 있는 애플리케이션에게 데이터를 전달해줘야 하는 것을 알 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Application Layer 까지 오면 웹 서버가 사용될 HTTP Request 데이터를 얻을 수 있게 된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이제 서버에서 정상적으로 HTTP Request를 받고 응답을 돌려보낸다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/xUMjGaZ.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"“\/“에 매핑된 GET요청을 처리해서 적절한 HTML을 응답해준다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"실제 크롬 개발자 분석도구를 통해 확인해보면 아래와 같은 HTML을 받았다는 것을 알 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/wS3ayk5.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"HTTP의 요청과 응답과정이 끝나면 연결을 종료해야 한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"여기서도 TCP의 컨트롤 비트가 사용된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/HSHGWTx.png","type":"image"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 단계에서는 ACK, FIN 플래그가 사용된다."}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/731dS8b.png","type":"image"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"클라이언트가 서버로 연결을 종료하겠다는 FIN 플래그를 전송한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"서버는 클라이언트에게 ACK 메세지를 보내고 자신의 통신이 끝날 때 까지 기다린다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"서버가 통신이 끝나면 클라이언트로 FIN을 보낸다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"클라이언트는 확인했다는 의미로 서버에게 ACK을 보내면 연결종료가 완료된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"총 4단계에 걸쳐 진행되고, 이것을 4-Way-Handshaking이라고 부른다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/dPBXUrU.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그런데 서버가 FIN을 보내는 과정에서 한가지 문제가 발생할 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"서버가 FIN을 보내기 전에 보냈던 데이터가 FIN보다 늦게 도착할 경우에…","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/dZpfup8.png"}]}]},{"content":[{"inlineContent":[{"text":"서버로 부터 FIN을 수신했다고 클라이언트가 바로 연결된 소켓을 닫아버리면 FIN을 보내기 전에 보낸 패킷은 영영 클라이언트가 받을 수 없게 된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/txZ2OhB.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그래서 클라이언트는 서버로부터 FIN 요청을 받더라도 일정시간 동안 소켓을 닫지 않고 혹시나 아직 도착하지 않은 잉여 패킷을 기다린다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이렇게 4-Way-Handshaking 과정이 완료되어도 소켓을 닫지 않고 잉여패킷을 기다리는 상태를 TIME_WAT이라고 한다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"연결지향형 프로토콜인 TCP는?"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"TCP는 신뢰할 수 있는 프로토콜이라고도 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"요즘 세상에 우리는 엄청나게 큰 데이터를 주고 받는다."}]}]},{"content":[{"inlineContent":[{"text":"그래서 한개의 패킷으로만 주고 받기에는 상당한 무리가 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그래서 데이터를 잘게 쪼개서 보내게 되고, 많은 패킷을 보내게 된다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그리고 이러한 패킷들은 엄청 복잡한 인터넷을 통해 목적지로 이동한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이러한 복잡한 환경에서 과연 데이터들이 유실 되지않거나 올바른 순서대로 잘 도착할 수 있을까?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"바로 그 어려운 것을 신뢰할 수 있는 프로토콜인 TCP가 가능하게 해준다."}]}]},{"content":[{"inlineContent":[{"text":"TCP는 흐름제어, 오류제어, 혼잡제어를 통해 신뢰성있는 데이터 전송을 보장할 수 있게 해준다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/www.youtube.com\/watch?v=BEK354TRgZ8"}]}]}]}]}]}]}],"metadata":{"roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"title":"211229 TCP\/IP","role":"article"},"schemaVersion":{"patch":0,"major":0,"minor":3},"sections":[],"kind":"article","references":{"https://i.imgur.com/Ji52ep5.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/Ji52ep5.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/Ji52ep5.png","traits":["1x","light"]}]},"https://i.imgur.com/nIa3aUV.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/nIa3aUV.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/nIa3aUV.png","traits":["1x","light"]}]},"https://i.imgur.com/uJsN2C0.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/uJsN2C0.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/uJsN2C0.png","traits":["1x","light"]}]},"https://i.imgur.com/wS3ayk5.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/wS3ayk5.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/wS3ayk5.png","traits":["1x","light"]}]},"https://i.imgur.com/86md72e.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/86md72e.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/86md72e.png","traits":["1x","light"]}]},"https://i.imgur.com/4ynNjrF.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/4ynNjrF.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/4ynNjrF.png","traits":["1x","light"]}]},"https://i.imgur.com/t0qsmsO.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/t0qsmsO.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/t0qsmsO.png","traits":["1x","light"]}]},"https://i.imgur.com/XMqsm2L.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/XMqsm2L.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/XMqsm2L.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]},"https://i.imgur.com/dPBXUrU.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/dPBXUrU.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/dPBXUrU.png","traits":["1x","light"]}]},"https://i.imgur.com/HSHGWTx.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/HSHGWTx.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/HSHGWTx.png","traits":["1x","light"]}]},"https://i.imgur.com/txZ2OhB.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/txZ2OhB.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/txZ2OhB.png","traits":["1x","light"]}]},"https://i.imgur.com/cAGkkvb.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/cAGkkvb.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/cAGkkvb.png","traits":["1x","light"]}]},"https://i.imgur.com/Z375cPd.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/Z375cPd.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/Z375cPd.png","traits":["1x","light"]}]},"https://i.imgur.com/xyBWUo9.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/xyBWUo9.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/xyBWUo9.png","traits":["1x","light"]}]},"https://i.imgur.com/S5S7xjv.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/S5S7xjv.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/S5S7xjv.png","traits":["1x","light"]}]},"https://i.imgur.com/79Nhvhh.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/79Nhvhh.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/79Nhvhh.png","traits":["1x","light"]}]},"https://i.imgur.com/IAKcFA1.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/IAKcFA1.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/IAKcFA1.png","traits":["1x","light"]}]},"https://i.imgur.com/rG7YJ10.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/rG7YJ10.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/rG7YJ10.png","traits":["1x","light"]}]},"https://i.imgur.com/BQRrwPs.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/BQRrwPs.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/BQRrwPs.png","traits":["1x","light"]}]},"https://i.imgur.com/Hqb9XiF.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/Hqb9XiF.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/Hqb9XiF.png","traits":["1x","light"]}]},"https://i.imgur.com/vHyWDtp.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/vHyWDtp.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/vHyWDtp.png","traits":["1x","light"]}]},"https://i.imgur.com/xUMjGaZ.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/xUMjGaZ.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/xUMjGaZ.png","traits":["1x","light"]}]},"https://i.imgur.com/731dS8b.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/731dS8b.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/731dS8b.png","traits":["1x","light"]}]},"https://i.imgur.com/dZpfup8.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/dZpfup8.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/dZpfup8.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/xBJ4FOx.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/xBJ4FOx.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/xBJ4FOx.png","traits":["1x","light"]}]},"https://i.imgur.com/dtxFR1e.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/dtxFR1e.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/dtxFR1e.png","traits":["1x","light"]}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211230_http,_udid,_uuid.json b/docs/data/documentation/arinote/211230_http,_udid,_uuid.json new file mode 100644 index 0000000..c7f82a3 --- /dev/null +++ b/docs/data/documentation/arinote/211230_http,_udid,_uuid.json @@ -0,0 +1 @@ +{"sections":[],"kind":"article","metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","title":"211230 HTTP, UDID, UUID"},"schemaVersion":{"patch":0,"minor":3,"major":0},"variants":[{"paths":["\/documentation\/arinote\/211230_http,_udid,_uuid"],"traits":[{"interfaceLanguage":"swift"}]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/211230_HTTP,_UDID,_UUID","interfaceLanguage":"swift"},"primaryContentSections":[{"kind":"content","content":[{"level":1,"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","type":"heading"},{"inlineContent":[{"type":"text","text":"12월 30일 (목)"}],"type":"paragraph"},{"level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading","text":"학습 내용"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"HTTP 활동학습"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"은행창구매니저 STEP4 진행","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"level":2,"text":"고민한 점 \/ 해결 방법","type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[개발자 도구에서 헤더 구경하기]","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"크롬에서 F12누르고 네트워크 탭에 가면 볼 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/Qv6XS1E.png","type":"image"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"요청헤더, 응답헤더 등등 어떻게 데이터를 주고받는지 볼 수 있다."}],"type":"paragraph"}]}]},{"inlineContent":[{"inlineContent":[{"text":"[포스트맨에서 가짜 API 맛보기]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Mock Servers 버튼을 누르면 가짜 API를 구현해볼 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/mj12CRI.png"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"SET이나 POST를 사용해서 example도 만들어볼 수 있다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그래봤자 Mock일뿐…"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이것저것 뒤져가며 활동학습 마지막 퀴즈를 풀어보았다."}]}]}]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[API 만들기 퀴즈]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"heading","anchor":"%EC%8B%A0%EA%B7%9C-%EC%82%AC%EC%9A%A9%EC%9E%90-%EB%A5%BC-%EB%93%B1%EB%A1%9D%ED%95%9C%EB%8B%A4-%EC%82%AC%EC%9A%A9%EC%9E%90%EB%8A%94-%ED%94%84%EB%A1%9C%ED%95%84-%EC%9D%B4%EB%AF%B8%EC%A7%80%ED%8C%8C%EC%9D%BC-%EC%95%84%EC%9D%B4%EB%94%94-%EB%B9%84%EB%B0%80%EB%B2%88%ED%98%B8-%EB%8B%89%EB%84%A4%EC%9E%84%EC%9D%84-%EA%B0%96%EB%8A%94%EB%8B%A4","level":3,"text":"신규 사용자 를 등록한다. 사용자는 프로필 이미지(파일), 아이디, 비밀번호, 닉네임을 갖는다."},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이미지는… json에 담으려고 base64로 인코딩했어요."}]}]}]}]}]},{"code":["`POST` `http:\/\/ios-4th.yagom-academy.kr\/users`","`application\/json`","----------------------","`{\"name\": “Ari, \"id\": \"leeari95\", \"passward\": \"123456\", ”profile_image\": “iVBORw0KGgoAAAANSUhEUgAAAFYAAABXCAYAAACeCrJSAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN\/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz\/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH\/w\/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA\/g88wAAKCRFRHgg\/P9eM4Ors7ONo62Dl8t6r8G\/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt\/qIl7gRoXgugdfeLZrIPQLUAoOnaV\/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl\/AV\/1s+X48\/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H\/LcL\/\/wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93\/+8\/\/UegJQCAZkmScQAAXkQkLlTKsz\/HCAAARKCBKrBBG\/TBGCzABhzBBdzBC\/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD\/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q\/pH5Z\/YkGWcNMw09DpFGgsV\/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY\/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4\/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L\/1U\/W36p\/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N\/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26\/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE\/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV\/MN8C3yLfLT8Nvnl+F30N\/I\/9k\/3r\/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt\/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi\/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a\/zYnKOZarnivN7cyzytuQN5zvn\/\/tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO\/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3\/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA\/0HIw6217nU1R3SPVRSj9Yr60cOxx++\/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3\/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX\/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8\/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb\/1tWeOT3dvfN6b\/fF9\/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR\/cGhYPP\/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF\/6i\/suuFxYvfvjV69fO0ZjRoZfyl5O\/bXyl\/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o\/2j5sfVT0Kf7kxmTk\/8EA5jz\/GMzLdsAAAAEZ0FNQQAAsY58+1GTAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAACkuSURBVHja7J13mGVVlfZ\/a+9zzo2Vu6qrc6BpmqZpcpAmGEAExASoKCLCjNqoDCAqjqPzmXVkTKMyhlFBHSPiAGJEDIggAgrS5NA5V7zphL3X98c51d3g6AgNzzczH6ef\/dStqlv3nvuetVd417tOi6ry9PEUHE8RsLOA9wG3AX\/7vx2\/\/2wFu\/vCH3nnR\/7kZ29571vevHTh3hdUgpRb73tg5UlHHvb5NInZ\/6CjGFn\/MIcdfRSVan3HRTUGnAb86perSDrbmT5QZ9999uGeu\/9AqzHKA\/fdyQtfeTaNySat1iRREKKqlMtlyj1d1Op12s0G3\/7SlxmaMYtqrZvpM2Zz\/+o1TB8aJGhlqAlJyOifOY142wSVvgH2WL4P5U7Kj354NZu2baQW1Vi27CDmLFqCFw+7YXO7Dez+S2bveCwiHPuKl588rbf3Ar\/pAWYfuB+33sf8of4aYTjEoYc\/jx9885PMmjWbelfPLsAKa9dv+l9lybsNrDGy4\/FzTn+5iMhVew2UGe4aw0oJYFLiCrMX7YuqR1VJ05Q0TR8F7P82X2929wU6meQrFYAL95o7nyP27uK5z57O2LZxACZGMnoH5sD\/R4Fyty12cjTfwi8\/7+9m9HR1X7J4WodTT1nAvfemtBspAHsffAjnvWPlXGAlUP\/iD3586XVXXrnqfzOwu22xo5vuZXTTvQBvmT\/QxTMP2c6yfSOarRZpEgPw3kvfN8Nau\/rQpUsv3mfW9Ddaa+96zotfHIgIiGCsRUSetthdjz\/8cTP\/etUVh\/XW6xfsNxtOPG4WqptIsw493YOwGYCL9xzuo8+vpRJWCKzFORe5LM2sNTTGJ8my7GmL3fXoKUcAK+f09vLsFcK0XodLHNX+AMQB6PSBwfNmlDLOX3kU03rMlHUGWzc8xNoH7+eH3\/kPGhMTGGOettip48Pf+vozuyqVVx80q8MzjhygncSICHv0W5RJzn3JS+bdedPNvOTkJvsv90QSUw4jOnF89KbVI9e0k4hmI8baPw\/q1bfeP\/fqW9++B1ABQsAXv8qK5cmzzq3cfs8qQP\/xvDf5\/9HAAiuP2mcvTngO1OujxA1HJgEL5kT09Hp++bOfsf\/8MiccOxtrNjGtXzlkwT785M6bVjpjroFwR8r22X\/\/eF\/xml3A84F9gZ5KFJ4+PH0QUVA8ooICYgQxuX\/23rN16zYandyvv\/uT\/3IccPMFf\/s3k\/9Tga1G5e0s2d+SxhN4CYizlN5By7K9ZnHTb3\/HM4\/di+6aoq7NnLlt\/rAmASi\/9ZIPzQFqwIH\/9uOrV5ai8pGKJ7IB82fOhizDaEbZZJisSRgKViwmMBgjNJuTtBoVvPOkLmF2tUqrUqdUChlP0p+MjI\/xsc9\/4UrgUmD9ea96zar\/KcCeuu+SJc8\/YI+NTOudTpqUMb5E5jPSxHHUUePUuiIOPTwhi2PCqMSee0H8w\/X0dHc9W5A1RgJKxrLXrEGSbIxaJaOn7pgxvJnhmcL0IcvMmTX6pw1RrZQphRVKUQ2RMtu2jzM6to3MZ7TaTbZsabJ2Xcr99ytrHqkxLeglqPS8eO3I9hcDfPIrX\/oocCfw9fdc9Pfxf2dgZ5UDWDTPUrIpSWYQo4gRWi3HgrmOxYv3JG1N4NIOgVWWLplBf2kbS0oDbBvZTpyNsHRBmf2Wl1m2\/1wW7dHD\/Dl9DAzUCKMOJmiDtIFOXmCIBSeoV+bOnQZSB2vzn1OFrMTouLB+XcIf79zCddfdTXD7GFk2QF9t9oVB2fLAmnVfetclHzjha5d\/7Yf\/XYGdYV2bOTMM6loYH+FMGxCML2PTUVzawLgugmAczRwDvQMcdWSdb\/\/oLp59xDJeeMp0lh8YMmtWiqm0oT2BppO41JM1LXiL9wbUFlmD4jUGmwJt1MUIEUZqpG6coJxRrzZZsqzNsoNqvOCUfbn3j\/tx868n+MVPHmHtmlHs4Ay2JJ0fvPLMV94IXPral5zy1ScbWFHVJ5qcLxwc6H\/wlUdFvP506OvtEHdKOJPhvUVcnVAUJcEYwRjFecEHIUlYwocRM+bMo6tboO1IOx08TfAW0RAVQfJLhKKgnuIHeHFFEqCICxENQATwKIqKR3B4gSAsE5ZrYCytRsrG1Vu5\/td3c\/0NFTZs6eJ3D2yg0Wyc9YKjn3FZLao\/bnbr1W94zZNusUFghHrUprtnkDSNQUBciBEDQYrXDDGCR\/AY1ISElRqDwzVKtQqpn6A9lmCyACMBRst4A1pc6By6Imvace2VHQijqFDkywri89+ogBosBhfHaBYjBkKEhXvU2WPvk3j5aQHX\/+pBvnD5JLfcE375ql\/+Zunpxx73tv8OBUJFcAz2lojCCEeE09wFiAZgm5hyBycJiVc0snQPTmNo+hysVIhbMb6TYrIQYwyqHrQKGLw4vPhdluKFYgmKKZZFjaImRU2MSoJKAuLInwUWwagizqMupdWZIN02RlVWc\/KLUz770QM448S9md4\/8Nav\/\/QnH\/vvAOwLhmr9DHaHuCglkQ7OZuAy6pUKttzLWKtGol3UBwYZmD1ApV9IgjE8AdaXMS5EjAHTgaCJMy28dEBikA5Iki\/SXWoCQA2iJrdMiVHJUCwQoVpGCcBYfGHvGZaMEC8WMQrlUTRTdHOd4TljvO3tZc56YTfD\/UPnv+PjH3rX\/2tgV86teqrVDFVDpgZPRl\/vEONbynz\/ytU88PAEM+bPonegBwlLOEpgyqhkiMkw1qHqcS4CqeFFUbWIL+VLc\/8pmGLzKwaPwSFTy1cQLSFqcvDFoZLh1OXWbQyI4HGoKEJAhsGFliQQ0smMgR7Dm1buywmHhgxPm\/buM9941pn\/r4B99YyB\/hnr1j9CvebBC8aW6Orp4557NnDFt1ZRry5hxdHHUOmqotbjNcT5fIl4vCQgGcYIImWyrIQKGCLQUrEi0BDUggqimi98AawHLefP2+GEM8CBgfyZ5AHN5I+MhjgsmRFScXhfJZ4cZ9a8ES46fzEHzO+jXCpfdtYbzl6608\/\/+fVkpls95VLpyyY1dKpdDPUJAzpJLQq56roSP7tuDS962X4cd9ogpXaTpNkBGxGg+YfWrAhBJdzUmZNgZOpxtkugekzjDkEf80tnWwXIIK5euI\/CnQBCfkFCFURyv1zOyK3bCBmKEtAZb7N0\/y7Oe+0gmy7pcPt9a1ci6Zua7s+ezpNusafMnTHEKStiPvx3Q\/TPrbA+XcSXvtfmu1fewymnzuf5z1+KbRgmkknS0PNUMq1GBeMNxhtEOohkhdOwoKaw+DKK4shwOLwBZz2ZTXBW8KaE8yWS8UmOe94CXvGiEnvNm\/PGs97wutfDn+\/E\/qV20hMBdpoVz4lH9\/D8FwyxeUJ510dv5ds\/muS1K4\/mhOcvRtP10FJ8YHFWdqvb+V8Dq0UgMwhp4QqmNqMpwLW5pQtgPE4cTjIySVBxYCyiZcQb1K3jNWftxYELuumq1y9943mve0EpiPhz68l0BUMDUUi5F276\/SYu+gdHHDs+9N69OOYopakdRD0V2oRxCTGCij6FwKaFjw3yQkKyHFBf3ukW8ODroB6xTdAMI4JoiJMWRktYX8FKBdeOGeiOufD8kA3vqfKL2xor\/\/YNZ9\/9y29fd\/9TXdJu9zbiqussv7rpbsoWPv7+vTj4sGnE0sJ7Q+gFNSllUyFx2ZNAp\/+l2tHvSMFUTOGHQchAp74DlTgvKVIwxhGEJSBErEWwePFo2gGNyFqWAw8d5MXHb8Kx5Hk33HbPSlW98Kn2sQ9tn2zx+e\/cS1Qp8dEPHs2KFXMIgyZO0tznqeCNQ51ieWq7AlPROS8eFM9UFHSIKgaDaAgS47VDudZDVJ3B2GQvq1cb7l2V8NBDjtFOGdszSFCukiQpmjY445XPYO9ZDQZ6ei445qXHXtgyYzQZedR6Mi32WZNtx7zZJS754L4cfGAHFytJUEJMh3ISokAnUqI0hacYWE8JbwyKywsNNYiCETA+xKURaqoQPEKlJ+Shh0e5\/vsN7r\/bkyW9OF9Cg1HC+ir2XjabE06oM2OoRHMEBvoTVp49k\/tXP8LvHqr\/8wmnnnr797\/1jeufChLm4nnD8z9YdWt59zvmctopB5FOjEKU0dGYEIN1JZw4OlGHKOnBquZl5lOUG3gJUSlyVwQhI8RAWsWrR6wnCxLKlen89MdjfO2b9zJv4VE848hns3jv5VS6emi1xrj73lu5\/qfXMbbhZs565RKOPqaMb23FVAf41Gfv5t++pvzhwTWo6uD3v\/WNbVPvf+JpL9tti13R39P3Qduc4M0X78lppy5HWyOghsQrYi04gxY+z\/qQzCh4fcqdQV4GeNAoLz4c4AwmVDJJKXeV+dE167j8K1s447Xv48RTTkVKpWI35VTkouUHc+xJr+Cqy7\/CpZ\/+IJVgFocc0o+m47zi9KXcdssdjDWm8cimred5Z9+VZcmT4mONiNwwp6+Xk46tc8bpe0A7wXcUDWJUI9RVSY3iRLE+otbpIrMJSZAgT2Ema0iKSkwQtYgajFrEGLy0CesBd963ka9ctoW\/eeNnOOkVr8aHLTLdgmcMaAKTOMaJqvCy17+JM876LJ+5dA0PPwxEXfT3jfGGlXsxq7vD7Jkz3nny6aed8WQFr1fNGhymp7KW15+\/kFJZafu1pJVxnEAoMZGPibIy1ltUMrxtUXYZJeefwjRWMcQYFyCuhkgL4wLQELHjmDAm8ZYrv93kwMNO5zmnPo9Ut6FqCXSIxvqMu2+8lQd\/\/0ckUSy9KBOcdM4xLFx+Dl\/5xj3E7Yy4AQcdUuWMl3XT7ZUgCL7y4le9cnrg490CNhKRL3eR8JpXLWbp3l3E7TZKkLNImpPQxuTFporH43BTyXtRhk6Bu5NMUURB1AJFmrSDwH480NqcHNTcvyIOjOLUE1Z7eOiBCR5+0PPSM18JONQ5AlvhDzfezNlnvIqTjj2el7\/oNN72xgv5\/W9vRNQDEWf87etYvbGL3\/1uO6WuCpqM8tKXHsHyxZb5w4MA\/2hl9yz23LkD0zlwH88pJ89Bm5vwKRjfi2R17BTlrIqXFGcy1ChebBG1wYvFSy7UEM0Bz1eepKMhKgZv0sdZTAieMmAQMkQcapKisiqhrs5dt48zd\/Yh9M+vgzYITRcjmzby92\/5O3768x8j7Q6bV2\/gi5+\/nHNOeQlXff1KIGLB0jksOeBEbrxhnKw9RpZm9PZ0OHflMrrircyaPrTyxDPPfukTBXZWT1fXx4JknFedPY2uni7idh2ROI\/CBQulJsbZJh6fEyFaBhOj2k2YVSinGWGm4PI800uIJ8zf3jQR00CIUd+VM1mPKyvwRcAU8JX8IqlibIJKzLoNo8ybfRi16iycLyHG8qOrf8CtN99KF4AxlAJLl4GH1m3mH952MffdfitgOPLI41nzYJUNqx026KXTarDi6B5OOXUe0m5hjPmmiOz3RIBdOXdoiKMP7eHoFQuJO+M5WYzJS0dpoxLn0dhXMRIW3KnJ\/0kGNkVDh9os7wwYn9fpJiWzSd5bQjAaEHjzhAKdigNJi4siuZCDAJd6kjShp7cXsRavAjhuuuFGUAhEQAypc1hj6A0tG9Zt4WuXXQ4u5YCDD6NUX8iD9yeYyIBNSDtjvP71h7D\/Xg3223MB5ArKxwXs0p7u7ne0tz3Ea169lEopIfUtsIJ4i4gD00JNG9Uy+K6clNaiNE88aTxBOx2jrS2c6eCtA+NRm+FMgrMJziiKxfiA0Oeu4nGiWgC7M1dW9ai3iIR4MtTkxIyxAXjH6NZtmCLrdd7v+Dvjcv3SL6\/7OWPbtlHurTM8Zx8euD\/FaYLYBJ+kDEwf411vO4Zk42pm9HW\/TkT+\/vEAu7Kv3M2Rh4QccriicY1AArxMFvW55HSc7wHTBjsJPkT9VrJkHXFngqTVodMQWg1L3HBoIyNqlSh1BgiyPvB1HCFOBCTF0kBylvbxuNkdnYNChJenXiIYk1GvR2zdsoYs3YpjAkxApbuWf3ClqNIMOAhVKQNrN2zg4fVrAZizZD4bNlaZjNOciPcBOtHmkGd0c8FbF1FOHb3d3e8XkTP+GmCX9XT3vlFa2zjl1CWUKwbXauN9njqpTH0ldwk+wvgQtIlmHpcKWZo\/r1IRukoRtaCfSGqYwBOUmkQmxjpP6EOM5nV+Zm3O+D8e5bekBTUY5e1vyc\/L+Q4myFg4f4iH7r+PZmOcQAQl4IADD9rx59Ya1PsdwdQBaeZoxJ2cyps1i4l2m2ZDiYIqIikkim+2eMWr9uW4Z\/XSH0WUougrItLzXwG7crhngL32gMOP2AOyDqpgJW+RPLot4YAQXICVGJcJmoUEQYWungFcFnD3H9dw3Y\/v4tprb+fnv\/g9Dz\/0CDYwVKIyoStIG4HUBGDM49Q5ZMXuiR51VmI86tssWzqXybGNrHvgIQyWjJQXn3Ya+y7bhwYe5z3WGIwIXhQH9Az0MThzOgDdfQOk3pMmgvMGjMv7ZnFMpTLCm992DAP1Mab19QFc8JdK2tBae242OcHzzuhm2vSAbLsBE6J4rJKz8lPXRAKQBDG+yBK68NKk1DvGjTcarrqiydbRQXqHZhAG\/WwfadGevJfDD9vKWWd20z+Qoe28fZ6alNDnLeu\/1maNjzBaKGSI0KIXJlTI2imz50Ys2XsL13z15+xz8HNBRpm+YD7vvuRDnHvOa3lk\/UZ6jCFVj1PIxPDys17FgnmL8t2TVAhNBQnamKCE0xCnTcRGtMYCFu87wrlvmsEH3j+ONfYfReQyVX34P7PYZ86fPZtplTbPPmompBNkIjhr8V4Jigg+ldgXpB0qkKpiuycI+2t8+UsZH\/vEfczb73je+Yl\/4Z++9Fn+6Uuf4CNf\/AivvfhdrFoznfMuvIVV9\/YT1vpA2pTUFc1F+autVgpQc79vUNFc9JEpLjaEIhx7fD933\/Et7rrpV4RUIG1x6PHH8ZFL\/4XFe+\/BVufZqmCqJc5\/y\/mcd\/75eM3IFEa2bEB8g2pF8K6DSAi2gsOT+nHiiSYvOmE\/VhzgWTRrCOAtf47dWrlo9pzPHLkg5XOX7YtIhzQNyLwlFId1KUp1Z\/4oeRtaFYwJsT0R\/3zJL\/j19Xvy9n\/8J57x\/OMAIWGykFkYDN10xsf46P95D7\/5xef40EcOYJ+l3XS2b0CCer47itGlXMyh\/2X8kkJt402a265GhZcQbLfn8i80+dVtho984Rv0D85C0xYSVlh93yp+cM21jI+Oc+SRK1hxzDFQrtHwCXVT5TMf+DDN9V\/iTRcPQ9qEzjSMJvgoIbMZZFCvT+euW1q87qKfMMYi7nrggU8+FtjQWpssHprBRef0c\/a5C2lPjpAZgzejlFydMKnhjcMHE6ikqB8o8kdHZcBw2aWbufo\/DO\/75BdZcsQynG9hk1LeNfUOrMl1WKUaLvV86B0Xcuevr+DTH3suA913EPs6GpRBpy7WXwbWSJy3YTTA2U5eeIgjNKPYuAehhIsMiVE+9ukH2bDpmbz1g29j\/twFJNomkmgHs5v7ak+cKDaKmByPueic03jZCY\/w3JP2IR1voj5Ag0Ze3REQZIbICLYS8Z3vZrz3w7+mGQ79SfCa313rYqC6nUOfUQeniM9L0MBXwUWoGJAOmQ9wvkaYJQSuTaW7yi03jPPVqzZz3rs\/wJIjDsBlDdRnqO3gTQDlCtiA1ESkSQcbwkUXv5eu6Yfxsc\/fDLWhvDLzGerzQkNVc\/XKDr2W5Nu+2C1etBBiCNaFWDoYTXDpAF6iXLSRGqJAeePr9mHOwPe55PVn8NDv7iCSnrzkdm1IGqSZJ3OOMIJASnz3U5fRr3dw6FFDZNk2nDRRsbm2RjNCzTAmJHVKmia86JQK571+EL9p058Au6gchiyYGzF7YRVSVxAlWlilKeQPDiFEtYTgCUNDe7LDpz+zihNecD5Hn3QC+DRnn4wgYRkTVsEpKIRhnTAKyTpNSv3TWHnBW\/nFTZu5649bCMtVRBRjbaEg5C8SM6q2aM141BQKRARDCRXJqz1Skk6brsoEF1+0gkOXG\/7+wlfwH1deRtzuYGwdoiphUCcIunFJyDe\/\/A1+9v1Pc+YZ+9HdA3Hcwlib7x7NCSejmmvOjMVlDqtbeM2Zh3Lh+cv\/xMdetd+ihSefepzw9ouXIp2Y1HXwVvHisD4i8CGeFt7WcN5ibYNyJeTq797FN67ci0\/\/+9fpnTGMT1uYMGerHn5gHTf96iYevuduQjz7P+MYVjz7CKrdNXyWK6zefu7r6Or8knf8n31JsiZGIrz3iEyNg8pjOl07swJnMuIgRq0SZJbIhVgPajxeYrxJEVfGK5jIUu6G71+1iSuuKDFt76M4\/phTWDBvD5zxbBxr8KOrrmHihk9w+pumc8RxgzTHRwi0HxeHeXfXtPPKs+i1oSHqI9QZgmpK0G0flW6VrLUnh77BvvvNwwaOJE3J6ausIPY8agpNlPdgDVmgNCbLfP+aSZ774jPpnTEDl45iwxo45eorvsP73\/dhHnxgNabdIQSk\/gVefsbLeecH3kl3Xw9Q5fiTX8an3v5jNqyJGZ5rybJcAjoFqsiuE6NmB8CBzzsWgUouSKacW69NcL6GUkZwGMAbQ8dbOuNNTjxxEfsva\/HDq7\/P9756FS6oUPZgY8fw7EnO+dgCFs6v0R7fhvFd+CxAjMNIskvlKWDyToKIYENFEyGbMI8CdnopjIjCjPlz66BJvhVFdtGk5qZvjMFNBZfQceetE4xOzOE5Jz8rZ+N9gqWbH3zvav7ujRexeesI3cZSQghQGo0Gn\/vXL9A\/1Mvb3vV2jPUsXrYMW5rJqlWbmTV\/JtnU+xcGqn+SB+TbXk2CEGBdiNGpXNrgTQyS5a7MCagDdQTGod6QTowza1A455wVTMbC+m0jSNMws7+P+vSMLFpPu9EAVyrydsXsKJuFR4t2cz2DiOIcmNQ\/ysceMtDdz+xhx\/BwCZ+1QQQRC2JQzZUkioI3GOMRYoIg5Y7bO8ycP58ZC8pkfpywVGXdgw\/ykff\/E9u2jjCzVKaEpSRCSaBuQiLgG5d9g9\/feitQomf6MD3D+3H3PVtwTlE1eF8otUQeI0XzOx4nURMVJUxqlOJebNKFuioqBmNaRDQpuZSQlKpt0q0j1AQiqUBcQtsbqbCOJbMT9tqzRWnaBhrxZuLRADpDGDcTQ5B3KsQVblOnkmjQYEdprWoIQ1AfP8piD50xMMiMwVG6u\/I44yjUfV6KTkAuiRSfYYIQiyCdkIdWP8ScRcdg6aMjbQIibvnF9dzxhzvoRTBJSlBQehmgJqMsho2rN3D3HQ9w4KHHEVaq9M6ewfp1Y2SxoCbvNKgaRAPUJDuBVVsQL55Me\/CSUS5NIChRYMEIYksQ2uJPKmS2TJylaJqSJd2oV7I0IwwnCAlIOh4jHQSh4gMCB5mJ8aYD0gJjUF\/NS2eJUVPIk3y0i3AkwXuDkeBRwPa1GhPMmtlFOYQs013ihd1p8eTzBHHaJizV8W2hNeno7ZsORIh6sqTDqjvuwHvN2Vn1j2rPeLRgcMBk+aYpiWVg5iAPP+hpt1MqXbYoEnawPQUvofn5FJIwk6YYK0ipH6c1WnGHuJnQGgvoNCdobN\/O+NhGRkZjtm0boTneJJ2ACSdsjuFNFy1h\/30WkTYbZAEYb\/FYYquIJDvzW7RwA26XLoc+hsGcUuWEjwK2y2UdhoZKSGBwsSBTLzBVNiIoHkeCWAFCjOsibVuCoAJqsaZM5mLSuDPFcuYnM7V9ZaqlopQkolKu73hWuatEnOR1RM6peoIgxDuTgys76Z88kHmqKN7WuOG3a7j2xxNI1iYbTWlsBFoZ1qWYtEnJB1SCEl31QaZR5qbb1tN\/2IHMmDUdn2YEaUon8BgNUCkRRynVrE2oimqlkJfGBbC7gDoFZjFUkn\/AnVlByRrz8nLomTYtAhPkWiiTwFRLGV\/oqcFbg9EQ4gwbOOqVgLHRrXl3JGtSrlaYOXcuIDgsTsCJAw9WTcGQOaJqQFd\/fUfdE2QhkuW0oaBg\/E6JvOwkpIUQNMN5h6sYTGsev7j0D4zdto2jl5SoxQklVUp1oVQWyqUykRp6eizl4ZR\/uXmQsSHP299pGR6E9vYEb3uIslGseoQOpayJ+hJOy\/kOMZMFRZkBj2bS8m5KVgjvcoJqCtjBwBq6KoZp\/V2g4ByILQS9OyxWESwpEYE3hNZjokkWLg64Y9UtpD6\/cQ4Y9l6+nHpXnfZkk5IEaJHy5KL3gBjHngvnsmDPubk8CMvIlgmiMCQIBLEeUcV7t0tnYOqrQQgJwwBq3fz0ynvZfOcIp67oYTgKCNMEUy6h5SY2sET00FuDqD6da+\/awvd+u4pTXjfM8oNruEYTay1mSgWuufRfXJWdUpOp9CrcqR6XqbkIU\/j8KRflciH11MzW0OAQlSilWg3AKcYEaDE3tav8TJF8lkCDPEmOmhx82CD33XsX9z+wlsAEeNfiwMMP5bnHP4sWnkTzgWIrYDF4b8iAFc86knmL98CT0U5T1j64jqHBfiqVElmW5PNaZuf2zyOyFGJsQ1Stc+d9o3zl327hgHnzmVlrQHs7lVpA76x+uuf2Up\/TS++cYSoza1y\/6mE+85MRBvfq4WWvOQqxDXyaq8yNtnIpnfE448HXCqB8IQ0tOF+t5pmAys5AqrmWIQ\/uKUhnB7DlUqmCug7leiEuQzGuG\/HlAtxc3GvxhNIiMymxGrJ2naX7DDJ\/+ijXfu7rQJWMHmoDs7noHW9mxQFLaGiCZIpVg0do0ubgQw7gzHPfgLPdGEpMrHuEB+++gWXLyoRBSkiEtx5PQtmnhFk+6KEIZU0IwpTJ9hjffe9qljcTjliwlZG2oLMqlBeXKHVlRK6fih0gMWN89Fcpn7mtgVQT3vCaKktmNTBjJZwEOJMVEz+9iKsjanHBeKE5c4V8qbBKaRb9NZPnzMUsRb6bO4X9RTuADdMkwXtPEOQNN0R5NJsoqOSRPcwsJUA0JXOOap9w0gsG+c6\/f5o7b\/k9kTXEvsVe+x\/OJ\/7ty5z60tMxvf2MG0+7mnL885\/Hhz79Gfbcaz\/SwiB\/e8ONdDqr2Xu\/WXlDwiTY2BKkFbyGJFbJBFQiOjbGlGdx7RVr2fT7h3jG3n1kcYPhgRpzB2dQo4a6lKS7zvqkh8uu2cTo7Q9z4HDE4uWDPOs5y3BJiksVEYMpNBB5NeGLILl7sqgpHxs55zDGElibD\/1qtjOS71JlKGAzkweWALwKcXOE5x6\/hO9+8z7eceFFfO7bX2Z4eJBW1mbJAYfxicu\/zD133sn6dffTN1Bl6YGH01UbYkJTKhLSGJvgS5\/\/Vw49pMoeS6eT+q1gIMwi0DKZGDJSrAnxGZT6K9x\/5xau+dp6jp5Tpiew9PT3MTB7GlFQI44dkx3HjQ89yA2\/adJTrvK8g3q4\/p4NHHPK4fRNr5HFkxgT4pxi7K7pnNul0nwSgM2ZqABrgmKaxxc9\/10DYO4SQglJHWQWvKRIUqdaK\/Huj\/Rz5uk38rrTzuK9n\/ooe+y7nIkMTCli6cEHsPTgg3ac+rhr0mUtASGfeP8lrL\/3Nt73zvmUKqO4liHKhshskySK6QTQ01EC10CDjJhpXHb5bUwfFZ67f0zPUEYwb5C74ojV9yfc\/cAEa7dOUlXH4TM9R8xt8701NbYO1XjO8SGeCdIsRShjLXhNEDFFp7eQhGr0pACbxz4H6nOydyrb3HHldilpY9uEoJqnXqaBDbrpNJosnDODD3\/IcsGbr+PsF72CT37+Mo449qDi+icoUaHTgi5bY3zTCB\/\/wLu44quf5D3\/sB\/LFs0kbjUwKN5OktkMjxBmEYEzpAaCvl5+fq1nyw1befW+85mcVeXeiZC7rl3Hmg3bqWcwqw7Lh0PmDvbQG1XZ3Bjj99vWcOLZB9M\/HNJpjiE2Qp3Lc3X1T7p+dwrYWAS892ROwBSOWj2I2QVccqLB5OWk95orC9URmpB0osmKo2dx6adO5p8vuY13nv8qnvm8F7Li6ENZuPdMKpU+XFZi0+aH+M0vbuY\/vnkFzc138J53HcmLXjITPzmKweTlrMSoCRBjEOdxDmypysSE8sOv384+\/XWamzfxo\/uUySSgu97LfnsuYGF\/m2GbUDGQNjK8C7np\/pjuRTVOPPUg0uQudGoYT0A1xZidqdyTDWwnLIUIbVy2C5CPbehJPpmt2pX3o3DgqjmPIClOMvzEJAcdVObTnx\/i2qsn+ekPLuema75KqnWCABwd4vYE1ZLjqBUDnH7mchYtCPHt9TjrUA0QEQIVEtdFhodwnKYN6YkiHvjxw\/jfjzLZ1cWtGrKoJ2Fxf0Q9Sph0E4xtL\/O7SUMcOxqp8mBjnK0ywLnnLKbWt57OiCJEO1v3kuWjSlrMhWHgSZjymQJ2W5KkaKlMJ\/Z5S1vZmQAjO4QQoFjtYLUocEXwks9OKSCa4RsT9FWHeOUZCzjphYb1j2xj9eqtdNopmTMMD81n4aJhZs7txtCmPTmWbwebFcUIqIZ4gch7SrFDyiGbR0L++YubuG8LHGodC+cFrLMBq7Y06biERkUomSaVmlAZhso02GdmyBEHLmbxwUN0JiYKUHVnZ1cfO4FjnpS5tClgN2zbto20ZwaTk3GuwzIBFAORO8cti0Rdi1H1HfxBPk0hxkKWh6e4leBtm66yYZ\/9LfvsNzdPpE1xP65Oi6S1kSxNMWGI94rDERiP+GK0U1JEPdZH+I7y61\/eTCMeZdkh0xjqzUjNBJXBPmbPnEP\/zG769+hjuL+HrpqlWnOYWkIUZZi4Q9waAWcKQmlX5Owu\/tU\/hmfdfWC3J5ljspMxMtaCIMJrsTOY2hmykwfVLtAYyBNlb\/IKxDtLRIINM1Q9ma8TdzJMpwXSzPtDCKLlQjAsSJDTbdbmpLq6FKsGo5DZFhkRbfoIWyn7zRvmUx9fjq9PEtmU7qhMV70OtpKHAzeJ8xvxqtg0RMZSEhzNyFNNLZYAZ3bh2dSw8zZexfCdml2I9N0HNvHek3kYn2gUpbEthop37dzn32a2lfePFNQ28HTh1WIlJnAWXISXDA0SFIP4MniHiEGw+Q0bCuswVFAHpEIoAWp8obdNKMV1vFhi20HrLRYs68NIiVQ9YRjgUkenNQFZM78Bj3FgFacOQ4aYEEdI1Zl8gtEmBWO3U9SXTzCmxWBzmluwhrsNrtklQf2CiSqMbY+LPHYn1Se6S0ArSjyV\/LYhiikG1QrSJL\/lGCoGUVdIz3N9gFD0sCTvYQlTt3oxWGt3OJ0dQ8YF6Wo0byomaYe0PYJL2sStJmnSwTuPEU8Q5hYvCEFxk7O8LV50mR+t7NqlpbKLPP9Rn5MnLY+9aTLxf7N1Y4bGKYHxpJIiGmBUyMQipDkRqMEOAMRXi62Vz7A6ozuumdWdfiv\/bAUfq7KLwDinBp3R4jJPES0RWUEbWgWyPOg4wy6vl9\/fwKP5TJcUEVZ3zjrkXPCUUPyxgcnl3YEdKc8ugW23p3l2Hvds27aNkXFDs+MgFPAWCHcy43k3\/T8ZunxShmL\/bIv7L7+PPonv++TN9+yK0gNxlrJpizI6mmBMlt80p6iUHssZPH389cBujZOYrWPC9m0xBB6RoMhhNfeV+kTnmv\/\/Btar6u8Suli7LoEgRCiExlKkIuKfRuwJAAtw1cbRBo+sbeGT4vbOonmcF30ccuCnj8cC+8PxVod7Hxin1TIEtmhBCzvSpqf\/C5UnBuyW1HlWr03ZukmxJYPH5Roq8ntkPQ3rEwN2W6vdYssErHmwAzbIbzjmC35AbBHQnj4eL7DNJEkuGGkb7rh9G2jeA\/OaS\/1Un063niiwAOM2KLHqD2vIGi1CGyASFOpqeTrd2g1g\/9CIW6xa28XD6wJsuYpkHZzJKxT7tJN9wsDetn7jpqtH2jV++7uHkKBEaIWp+72pc0+j9gSBBWh29w5xyy3r8alDxaIaYY0F+3SRsDvAfmHD+Gbu\/KNlzeoGpQqEAfgsRZ52BbsF7M9Wb1zLtu0Bf7xzK6YUkrmEILRPZwW7CawquqUUDfKbmzbj8RhrSZ3bRST39PFEgAW4dISMG3+9hc3rDGHJ5vNN8rTF\/jXH\/x0ASNMHrwxw5fkAAAAASUVORK5CYII=”, }`"],"syntax":null,"type":"codeListing"},{"items":[{"content":[{"level":3,"type":"heading","anchor":"%EB%93%B1%EB%A1%9D%EB%90%9C-%ED%8A%B9%EC%A0%95-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%98-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%A1%B0%ED%9A%8C%ED%95%9C%EB%8B%A4","text":"등록된 특정 사용자 의 데이터를 조회한다."}]}],"type":"unorderedList"},{"type":"codeListing","syntax":null,"code":["`GET` `http:\/\/ios-4th.yagom-academy.kr\/users\/1`"]},{"items":[{"content":[{"level":3,"anchor":"%EB%93%B1%EB%A1%9D%EB%90%9C-%ED%8A%B9%EC%A0%95-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%98-%EB%AA%A8%EB%93%A0-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%88%98%EC%A0%95%ED%95%9C%EB%8B%A4","type":"heading","text":"등록된 특정 사용자 의 모든 데이터를 수정한다."}]}],"type":"unorderedList"},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"바디안에 포함되지 않은 기존 정보들은 날라간다."}]}],"style":"note","type":"aside","name":"Note"},{"code":["`PUT` `http:\/\/ios-4th.yagom-academy.kr\/users\/1`","`application\/json`","----------------------","`{\"name\": \"Arijjang\", \"id\": \"leeari95\", \"age\": 20}`"],"syntax":null,"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"anchor":"%EB%93%B1%EB%A1%9D%EB%90%9C-%ED%8A%B9%EC%A0%95-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%9D%98-%EC%9D%BC%EB%B6%80-%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%A5%BC-%EC%88%98%EC%A0%95%ED%95%9C%EB%8B%A4","type":"heading","text":"등록된 특정 사용자 의 일부 데이터를 수정한다.","level":3}]}]},{"type":"codeListing","syntax":null,"code":["`PATCH` `http:\/\/ios-4th.yagom-academy.kr\/users\/1`","`application\/json`","----------------------","`{\"name\": \"Ari\", \"id\": \"leeari95\", \"age\": 20}`"]},{"type":"unorderedList","items":[{"content":[{"type":"heading","text":"등록된 특정 사용자 를 삭제한다.","anchor":"%EB%93%B1%EB%A1%9D%EB%90%9C-%ED%8A%B9%EC%A0%95-%EC%82%AC%EC%9A%A9%EC%9E%90-%EB%A5%BC-%EC%82%AD%EC%A0%9C%ED%95%9C%EB%8B%A4","level":3}]}]},{"syntax":null,"code":["`DELETE` `http:\/\/ios-4th.yagom-academy.kr\/users\/1`"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"anchor":"%EB%93%B1%EB%A1%9D%EB%90%9C-%EB%AA%A8%EB%93%A0-%EC%82%AC%EC%9A%A9%EC%9E%90-%EB%A5%BC-%EC%A1%B0%ED%9A%8C%ED%95%9C%EB%8B%A4-%EA%B2%80%EC%83%89-%EC%A1%B0%EA%B1%B4-%EC%95%84%EC%9D%B4%EB%94%94-%EB%8B%89%EB%84%A4%EC%9E%84-%EB%93%B1%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%EB%A7%8C%EC%A1%B1%ED%95%98%EB%8A%94-%EC%82%AC%EC%9A%A9%EC%9E%90%EB%A7%8C-%EA%B2%80%EC%83%89%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8B%A4","level":3,"type":"heading","text":"등록된 모든 사용자 를 조회한다. 검색 조건 아이디, 닉네임 등을 이용하여, 만족하는 사용자만 검색할 수 있다."}]}]},{"type":"codeListing","code":["`GET` `http:\/\/ios-4th.yagom-academy.kr\/users?id=leeari95&name=Ari&age=20`"],"syntax":null},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[퀴즈 풀면서 궁금했던 점]"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"궁금했던 의문점들을 "},{"code":"노루","type":"codeVoice"},{"text":"가 세심하게 답변해주셔서 기록…!","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"어.. 비밀번호면 https여야하지 않나?"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"비밀번호는 https 라는 인식은 조금 인터넷 사용팁에 가까운 질문이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"http와 https를 나누는 차이는 uri명세에서는 드러나지 않는다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"http보다 https가 좀더 안전하고 신뢰할 수 있지만, 기본적으로 http\/https 모두 동일한 프로토콜 규격으로 여기면 되겠다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이미지를… 파일로? Content-Type을 뭘로 해야하지… JSON으로 꾸역꾸역…","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이미지를 base64로 인코딩 해서 사용하는 기법은 과거에 인스타에서도 사용했던 기법이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이미지를 로딩할 때 네트워크의 영향을 덜 받기 때문에, 범용서비스에서는 부가적인 조치로 사용하는 방법이지만 일반적으로 파일의 경우 ","type":"text"},{"type":"codeVoice","code":"multipart\/form-data"},{"text":"를 Content-Type으로 사용한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"base64 String을 파라미터로 주고 받으려면 mimetype을 포함해야 한다. "},{"type":"codeVoice","code":"data:image\/png:base64"},{"type":"text","text":" 이런 단어인데 보통 base64를 다시 디코딩하기 위해서 레스터 포맷을 추론할 수 있어야하는데, 이 포맷이 없는 경우 해당 이미지 데이터가 올바른 색 정보를 갖을 수 없기 때문에 흑백으로 표기되거나 이미지가 깨지는 현상을 막을 수 있다."}]}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[활동학습 들으면서 메모했던 것]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"편지봉투는 반드시 있어야한다. (서버에게 요청시 양식)","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"클라이언트(나)는 데이터를 알 수 없다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"컨텐츠 타입은 바이너리 파일들을 기존의 시스템에서 문제 없이 전달하기 위해서는 텍스트 파일로 변환이 필요하게 되었는데, 이러한 텍스트 파일로 변환을 인코딩이라고 하며 텍스트 파일을 바이너리 파일로 변환하는 과정을 디코딩이라고 한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"인코딩한 파일은 Content-Type 정보를 파일의 앞부분에 담게 되며, Content-Type은 여러가지 타입이 있다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Content-Type에는 Key에는 메세지 바디의 타입을 나타내는데, 우리가 사용하는 JSON은 application\/json 이라고 설정해주면 된다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"타입을 어떻게 알려줄까?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Content-Type 정보를 파일 앞부분에 담는다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"브라우저의 경우 응답\/요청시 HTTP 메세지의 헤더에 정보를 담아서 보내게 되는데 이 헤더에 Content-Type 정보를 담아서 어떤 데이터 종류인지 알려준다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"text":"MIME-Type 구조","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"type\/subtype 으로 구성된다. (소문자 사용가능)"}]}]},{"content":[{"inlineContent":[{"text":"type은 1.개별 혹은 2.멀티파트 타입이 될 수 있다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"개별파트"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"text : 텍스트를 포함한 모든 문서 (인간 readable)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"image : 모든 종류의 이미지 (gif 포함)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"audio : 모든 종류의 오디오 파일","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"video : 모든 종류의 비디오 파일"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"application : 모든 종류의 바이너리 데이터"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"text\/plain
서브타입이 없는 텍스트 파일을 위한 기본값
서브타입이 있는 파일은 서브타입 명시되어야 올바르게 감지함."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"application\/octet-stream
서브타입 없는 바이너리 데이터\/문서의 기본값
알려지지 않은 파일 타입은 이 타입을 사용해야 합니다.
웹브라우저가 실행여부를 묻기도 하는데 content-Disposition 헤더가 값 attachment와 함께 설정되었고 ‘Save As’파일을 제안하는지 여부에 따로 브라우저가 그것을 다루게 된다."}]}]}],"type":"unorderedList"}]}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[UDID\/UUID]"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UDID(Unique Device Identifier)"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"기기당 고유한 디바이스 식별 값(불변)","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"확인방법"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"Xcode > Window > Shift + CMD + 2"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"identifier 값 확인"}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UUID(Universally uniqye identifier)"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"8-4-4-4-12라는 5개의 그룹을 ‘-’(하이픈)으로 구분"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"실행할 때마다 변하는 값"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"10^40개 이므로 앱을 실행할 때 마다 랜덤 값을 부여"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"ex) 12345678–1234–abcd–1234–123456789012","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"출력"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"print(UUID())"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"보통 고유한 값이 필요할 때 활용한다. 유저를 등록한다던지…등등"}],"type":"paragraph"}]}]}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/www.youtube.com\/watch?v=BEK354TRgZ8"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/babbab2.tistory.com\/70"}],"type":"paragraph"}]}]}]}]}]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"references":{"https://i.imgur.com/Qv6XS1E.png":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/Qv6XS1E.png","variants":[{"url":"https:\/\/i.imgur.com\/Qv6XS1E.png","traits":["1x","light"]}]},"https://i.imgur.com/mj12CRI.png":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/mj12CRI.png"}],"identifier":"https:\/\/i.imgur.com\/mj12CRI.png","alt":null},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/211231_inout,_async,_await,_thread_sanitizer.json b/docs/data/documentation/arinote/211231_inout,_async,_await,_thread_sanitizer.json new file mode 100644 index 0000000..ca8e980 --- /dev/null +++ b/docs/data/documentation/arinote/211231_inout,_async,_await,_thread_sanitizer.json @@ -0,0 +1 @@ +{"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/211231_inout,_async,_await,_Thread_Sanitizer"},"sections":[],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/211231_inout,_async,_await,_thread_sanitizer"]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"primaryContentSections":[{"content":[{"type":"heading","level":1,"text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned"},{"inlineContent":[{"type":"text","text":"12월 31일 (금)"}],"type":"paragraph"},{"type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"은행창구매니저 STEP4 진행 후 PR 작성","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"inout"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"async\/await"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Thread Sanitizer"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Thread Safe하게 코드 작성하기","type":"text"}]}]}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","level":2},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[inout]","type":"text"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"inout 키워드를 사용하면 파라미터로 전달받은 값을 참조하는 줄 알았는데, 아니였다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"값을 받아와서 대입을 하게되면, 참조해서 수정을 하는게 아니라 아예 수정한 값을 새로 덮어쓰기를 한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"옵저버 프로퍼티를 inout으로 전달해주었더니 함수가 호출될 때마다 옵저버 프로퍼티의 didSet 블럭이 호출되었다.","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[async\/await]"}]}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Swift 5.5부터 구현된 기능"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"추가된 이유","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Swift 개발에서 Closure 및 completion hendlers를 사용하는 비동기 프로그래밍을 많이 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"많은 비동기 작업, 오류 처리, 비동기 호출 간의 제어흐름이 복잡할 때 문제가 많다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"많은 비동기 작업"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"일련의 비동기 작업에는 deeply-nested closures가 필요하다."}]}]}],"type":"unorderedList"},{"syntax":"swift","type":"codeListing","code":["func processImageData1(completionBlock: (_ result: Image) -> Void) { loadWebResource(\"dataprofile.txt\") { dataResource in"," loadWebResource(\"imagedata.dat\") { imageResource in"," decodeImage(dataResource, imageResource) { imageTmp in"," dewarpAndCleanupImage(imageTmp) { imageResult in"," completionBlock(imageResult)"," }"," }"," }"," }","}","","processImageData1 { image in"," display(image)","}"]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"오류처리"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"콜백은 오류처리를 어렵고 매우 장황하게 만든다."}],"type":"paragraph"}]}]},{"type":"codeListing","syntax":"swift","code":["\/\/ (2c) Using a `switch` statement for each ","func processImageData2c(completionBlock: (Result) -> Void) { loadWebResource(\"dataprofile.txt\") { dataResourceResult in"," switch dataResourceResult {"," case .success(let dataResource):"," loadWebResource(\"imagedata.dat\") { imageResourceResult in"," switch imageResourceResult {"," case .success(let imageResource):"," decodeImage(dataResource, imageResource) { imageTmpResult in"," switch imageTmpResult {"," case .success(let imageTmp):"," dewarpAndCleanupImage(imageTmp) { imageResult in"," completionBlock(imageResult)"," }"," case .failure(let error):"," completionBlock(.failure(error))"," }"," }"," case .failure(let error):"," completionBlock(.failure(error))"," }"," } case .failure(let error):"," completionBlock(.failure(error))"," }"," }","}","","processImageData2c { result in"," switch result {"," case .success(let image): display(image)"," case .failure(let error): display(\"No image today\", error)"," }","}"]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Swift Result가 Swift 5.0에서 추가되면서 error를 처리하는게 더 쉬워졌지만 여전히 closure 중첩 문제는 남아있다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"비동기 호출간의 제어흐름이 복잡할 때","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"비동기 함수를 조건부로 실행하는 것은 고통 그 자체이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"예를 들어 이미지를 얻은 후 swizzle 해야한다고 할 때","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이미지가 있으면 바로 swizzle"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이미지가 없으면 decode후 swizzle"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"type":"codeListing","syntax":"swift","code":["func processImageData3(recipient: Person, completionBlock: (_ result: Image) -> Void) {"," let swizzle: (_ contents: Image) -> Void = {"," \/\/ ... continuation closure that calls completionBlock eventually"," }"," if recipient.hasProfilePicture {"," swizzle(recipient.profilePicture)"," } else {"," decodeImage { image in"," swizzle(image)"," }"," }","}"]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"그래서 위와같은 코드가 필요하다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"이 함수를 구조화 하는 방법은 위 코드와 같이 completion handler에서 swizzle 코드를 작성하는 것이다.","type":"text"}],"type":"paragraph"}]}]},{"type":"codeListing","syntax":"swift","code":["func processImageData3(recipient: Person, completionBlock: (_ result: Image) -> Void) {"," let swizzle: (_ contents: Image) -> Void = { \/\/ ... continuation closure that calls completionBlock eventually ✅✅ "," }"," if recipient.hasProfilePicture {"," swizzle(recipient.profilePicture)"," } else {"," decodeImage { image in"," swizzle(image)"," }"," }","}"]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 패턴은 함수의 자연스러운 하향식 구성을 반전시킨다."}]}]},{"content":[{"inlineContent":[{"text":"swizzle closure가 completion handler에서 사용되므로 capture에 대해 신중하게 생각해야 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"조건부로 실행되는 비동기 함수의 수가 증가함에 따라 문제는 더욱 악화되고 본질적으로 반전된 pyramid of doom을 생성시킨다."}]}]}]}]}],"type":"unorderedList"},{"syntax":"swift","code":["func processImageData4a(completionBlock: (_ result: Image?, _ error: Error?) -> Void) {"," loadWebResource(\"dataprofile.txt\") { dataResource, error in"," guard let dataResource = dataResource else {"," return \/\/ ⚠️ <- forgot to call the block"," } loadWebResource(\"imagedata.dat\") { imageResource, error in"," guard let imageResource = imageResource else {"," return \/\/ ⚠️ <- forgot to call the block"," } ..."," }"," }","}"],"type":"codeListing"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"실수하기 쉬워진다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"completion block을 호출하지 않고 그냥 return하고 잊어버리면 디버깅 하기가 어려워진다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"내가 만약 잊지 않고 completion block을 호출했다고 치면"}],"type":"paragraph"}]}]},{"type":"codeListing","code":["func processImageData4b(recipient:Person, completionBlock: (_ result: Image?, _ error: Error?) -> Void) {"," if recipient.hasProfilePicture {"," if let image = recipient.profilePicture {"," completionBlock(image) \/\/ ⚠️ <- forgot to return after calling the block"," }"," } ...","}"],"syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"completion block을 호출하고 나서 return 호출하는 것을 까먹을 수도 있다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"guard는 return을 하지 않으면 컴파일 에러를 주긴 하지만 항상 guard를 쓰는건 아니니까…","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"해결"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"위와 같은 문제를 해결하기 위해 async-await proposal은 Swift에 코루틴(coroutine) 모델을 도입하게 된다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"비동기 함수의 semantics 정의와 동시성을 제공하지는 않는다.","type":"text"}]}]}]}]}]}]},{"content":[{"inlineContent":[{"text":"비동기 함수(async\/await)를 사용하면?","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"비동기 코드를 마치 동기 코드인 것처럼 작성할 수 있게 된다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로그래머가 동기 코드에서 사용할 수 있는 동일한 언어 구조를 최대한 활용할 수 있게 된다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"자연스럽게 코드의 의미 구조를 보존할 수 있다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"언어에 대한 최소한 3가지 교차 개선에 필요한 정보를 제공…? 이게 뭔소리야","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"비동기 코드의 성능 향상","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"better performance for asynchronous code"}]}]}]}]},{"content":[{"inlineContent":[{"text":"코드를 디버깅, 프로파일링 및 탐색하는 동안 보다 일관된 경험을 제공하기 위한 더 나은 도구","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"better tooling to provide a more consistent experience while debugging, profiling and exploring code"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"작업 우선 순위 및 취소와 같은 동시성 기능을 위한 기반","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"a foundation for future concurrency features like task priority and cancellation"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[코드에서 경쟁 상태를 확인하는 방법]"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Xcode에서 ..","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Product > scheme > editScheme > Run > Diagnostics > Thread Sanitizer"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Thread Sanitizer 이걸 체크하면 빌드를 돌리고 나서 thread safe 하지 않은 상황이 발생할 수 있는 가능성을 엑스코드에서 체크해준다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"하지만 사용해보니 완벽하게 체크해주는 건 아닌 것 같다.[?] 그냥 도와주는 기능이라고 생각하고 사용해야할 것 같다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/ERrHYab.png","type":"image"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[Thread Safe하게 코드를 작성하려면?]","type":"text"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"공유자원을 읽고 쓰는 작업을 Thread safe하게 Shemaphore를 사용해서 하나의 thread만 접근 할 수 있도록 하는 방법이 있다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하지만 이 방법은 완벽하게 제어하기는 무리가 있다. 오히려 공유자원을 lock으로 처리하다가 교착 상황 발생할 가능성이 높다고 한다."}]}]}]}]},{"content":[{"inlineContent":[{"text":"Serial Queue sync로 보내서 처리하는 방법도 있다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"그러면 들어온 task에 순서가 생기기 때문에 다수의 스레드에서 동시에 값을 접근하지 못하게 하는 상황이 된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"sync로 사용하는 이유는 Serial queue로 보낸 작업을 기다림으로써 공유자원의 제대로 된 값을 얻기 위함이다."}]}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/zeddios.tistory.com\/1230","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/github.com\/apple\/swift-evolution\/blob\/main\/proposals\/0296-async-await.md","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/ios-development.tistory.com\/618","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/sujinnaljin.medium.com\/ios-%EC%B0%A8%EA%B7%BC%EC%B0%A8%EA%B7%BC-%EC%8B%9C%EC%9E%91%ED%95%98%EB%8A%94-gcd-12-c06b599fe7f5","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/docs.swift.org\/swift-book\/LanguageGuide\/Concurrency.html"}]}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"schemaVersion":{"major":0,"patch":0,"minor":3},"kind":"article","metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"211231 inout, async, await, Thread Sanitizer"},"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://i.imgur.com/ERrHYab.png":{"identifier":"https:\/\/i.imgur.com\/ERrHYab.png","variants":[{"url":"https:\/\/i.imgur.com\/ERrHYab.png","traits":["1x","light"]}],"alt":null,"type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220103_uicollectionview,_http,_urlsession.json b/docs/data/documentation/arinote/220103_uicollectionview,_http,_urlsession.json new file mode 100644 index 0000000..c73994c --- /dev/null +++ b/docs/data/documentation/arinote/220103_uicollectionview,_http,_urlsession.json @@ -0,0 +1 @@ +{"schemaVersion":{"major":0,"minor":3,"patch":0},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220103_uicollectionview,_http,_urlsession"]}],"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"220103 UICollectionView, HTTP, URLSession"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"kind":"article","primaryContentSections":[{"content":[{"level":1,"type":"heading","text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned"},{"inlineContent":[{"text":"1월 3일 (월)","type":"text"}],"type":"paragraph"},{"text":"학습 내용","type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"UICollectionView 활동학습"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"URLSession 학습"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"프로젝트 고민","type":"text"}]}]}]}]}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"text":"고민한 점 \/ 해결 방법","level":2,"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[UICollectionView 활동학습]"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"학습하면서 했던 메모","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"HTTPS는 HTTP프로토콜에서 TLS(SSL) 프로토콜을 사용하여 세션 데이터를 암호화한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"컬렉션뷰는 각각의 셀의 크기를 조정한다기 보다는 오토레이아웃과 estimatedItemSize를 사용해서 셀의 크기를 조정한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UICollectionLayoutListConfiguration","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"컬렉션뷰에서 리스트 모양을 보여줄 수 있는 클래스","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"UICollectionViewLayout","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"컬렉션뷰의 레이아웃을 커스텀할 때 사용하는 클래스"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"셀을 끌어다가 드래그 하는 것을 드래그 앤 드랍이라고 표현하지 않고 셀의 이동이라고 봐야한다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"드래그 앤 드랍은 테이블뷰에서 컬렉션뷰로 옮기는 것을 뜻한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"테이블뷰와 컬렉션뷰의 공통점과 차이점","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"text":"공통점","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"테이블뷰, 컬렉션뷰 둘다 콘텐츠 관리를 데이터소스와 델리게이트로 관리한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"스크롤이 가능하다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"셀을 재사용할 수 있다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"text":"차이점","type":"text"}]}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"테이블뷰는 가로스크롤이 불가능하다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테이블뷰는 한가지 모양의 셀만 만들수있는데 컬렉션뷰는 다양한 모양의 셀을 한화면에 같이 보여줄 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"예를 들어, 헤더, 메뉴, 본문, 푸터 각각 셀을 만들어서 원하는 모양으로 만들고, 하나의 뷰 컨트롤러에 셀을 조합해서 한 화면에 나타나게 할 수 있습니다. 이 방법을 사용하면 자주 사용하는 셀을 재활용할 수 있습니다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"테이블뷰는 액세서리뷰와 컨텐츠뷰로 나눠져있지만 컬렉션뷰는 배경뷰와 컨텐츠뷰로 나눠져있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"테이블뷰는 디폴트 형식이 정해져있는데 컬렉션뷰는 셀을 직접 커스텀해야한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"텍스트인 경우에는 테이블뷰를 사용하는 것을 권장한다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Consider using a table instead of a collection for text. It’s generally simpler and more efficient to view and digest textual information when it’s displayed in a scrollable list.","type":"text"}]}]}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"inlineContent":[{"text":"각 앱 화면에 적절한 뷰(테이블뷰와 컬렉션뷰 중에서)를 고르고 그 이유를 적어보자.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"페이스북 - 타임라인"}],"type":"strong"}]},{"style":"note","content":[{"inlineContent":[{"text":"\b","type":"text"},{"inlineContent":[{"text":"컬렉션뷰","type":"text"}],"type":"strong"},{"text":" ","type":"text"},{"text":"\b","type":"text"},{"inlineContent":[{"text":"이유","type":"text"}],"type":"strong"},{"text":" : 다양한 크기의 셀이 존재하고 다양한 이미지나, 동영상, 댓글 등등 셀마다 다양한 콘텐츠가 들어있다.","type":"text"}],"type":"paragraph"}],"name":"Note","type":"aside"}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"인스타그램 - 타임라인","type":"text"}],"type":"strong"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컬렉션뷰"},{"text":" ","type":"text"},{"text":"\b","type":"text"},{"inlineContent":[{"text":"이유","type":"text"}],"type":"strong"},{"type":"text","text":" : 다양한 크기의 셀이 존재하고 다양한 이미지나, 동영상, 댓글 등등 셀마다 다양한 콘텐츠가 들어있다."}]}],"style":"note","name":"Note","type":"aside"}]},{"content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"미리 알림 앱"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"text":"메인화면","type":"text"}]}],"type":"paragraph"},{"style":"note","name":"Note","type":"aside","content":[{"inlineContent":[{"type":"text","text":"컬렉션뷰 + 테이블뷰"},{"text":" ","type":"text"},{"type":"text","text":"\b"},{"type":"strong","inlineContent":[{"text":"이유","type":"text"}]},{"type":"text","text":" : 컬렉션뷰와 테이블뷰가 섞여있는 것 같다. (테이블뷰가 아닌건지.. 컬렉션뷰가 맞는건지 잘모르겠다.) 그리고 텍스트, 액세서리뷰가 포함되어있다."}],"type":"paragraph"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"미리 알림 목록화면"}]}]},{"name":"Note","type":"aside","style":"note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테이블뷰"},{"text":" ","type":"text"},{"type":"text","text":"\b"},{"inlineContent":[{"type":"text","text":"이유"}],"type":"strong"},{"text":" : \b셀의 구조가 테이블뷰와 흡사하다. 또한 왼쪽으로 스와이프시 \b편집 컨트롤이 나타난다.","type":"text"}]}]}]}]}]},{"content":[{"inlineContent":[{"inlineContent":[{"text":"iPhone App Store의 투데이 화면","type":"text"}],"type":"strong"}],"type":"paragraph"},{"name":"Note","type":"aside","content":[{"inlineContent":[{"type":"text","text":"컬렉션뷰"},{"type":"text","text":" "},{"text":"\b","type":"text"},{"type":"strong","inlineContent":[{"type":"text","text":"이유"}]},{"text":" : \b안에 들어가있는 컨텐츠의 구조가 다르다. 그리고 내부에 테이블뷰가 들어가있는 형태도 있다.","type":"text"}],"type":"paragraph"}],"style":"note"}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"text":"시계 앱의 알람 목록화면","type":"text"}]}],"type":"paragraph"},{"style":"note","name":"Note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테이블뷰"},{"type":"text","text":" "},{"type":"text","text":"\b"},{"type":"strong","inlineContent":[{"text":"이유","type":"text"}]},{"type":"text","text":" : \b동일한 구조가 반복되며, 섹션이 나눠져있고, 스와이프시 편집 컨트롤이 나타난다."}]}]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"text":"[URLSession]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSURLConnection"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Asynchronous event-based API","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"기능"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"Persistent connections (세션 연결 유지)","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Pipelining (이어받기)"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Authentication (인증)"}]}]},{"content":[{"inlineContent":[{"text":"Caching (URL 캐쉬)","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Cookies (쿠키)","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Socks and HTTP proxy (프록시)","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"WebKit?","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"오픈소스 웹브라우저 엔진"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"App Transport Security Settings","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/OE5wyTD.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"접속하는 사이트만 권장","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"URL Session Classes","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/HIaduZp.png"}]}]},{"content":[{"inlineContent":[{"text":"NSURLSession","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Singleton shared session","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"델리게이트 없이 간단한 비동기 요청"}]}]},{"content":[{"inlineContent":[{"text":"특정한 세션의 처리를 하기 위한 델리게이트 없이 그냥 간단한 비동기 요청을 보내고 그 요청에 대한 응답을 비동기로 처리하게끔 클로저를 넘기는 구조","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Default session","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기본 설정 세션 커스텀 설정 가능"}]}]},{"content":[{"inlineContent":[{"text":"configuration 객체에 값을 바꾼 다음에 세션을 만들 때 넘겨주면 커스텀 설정으로 생성이 가능하다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Ephemeral session"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"델리게이트 없이 비공개(private) 세션"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Delegate없이 비공개 세션을 만들 때 사용한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사파리나 크롬에서 시크릿 모드를 만들 때 사용한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"쿠키나 세션정보가 남아있지 않게 된다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Background session"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"백그라운드 동작을 위한 세션","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"앱이 멈추거나 앱을 나가더라도 앱을 사용하지 않더라고 운영체제 딴에서 백그라운드로 동작을 해주게끔 세션을 유지해주는 세션이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"지하철 앱이나 지도앱이나 이런 업데이트된 데이터를 받고싶어 하면 백그라운드 세션을 만들어 놓으면 그 세션이 유지되면서 네트워크로 업로드도 되고 다운로드도 된다.","type":"text"}]}]}]}]}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/fGyUKWc.png"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"URLSession은 Task를 만들어서 넘겨야 한다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"보통 요청하고 받는건 DataTask"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"파일이나 데이터를 업로드 할 때는 UploadTask","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"한꺼번에 여러개를 다운로드 할 때는 DownloadTask"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"대부분은 dataTask를 많이 쓴다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"URLSession은 7단계를 거쳐서 코드를 작업하면 된다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"세션의 configuration을 만든다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"위 설정값을 가지고 세션을 만드는데, 세션을 만들 때 델리게이트를 줄 수도 있고, 안줄 수도 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그 다음에 Task을 만들면서 URL을 넘겨준다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"요청을 보낼 때 델리게이트를 지정해서 쓰면 클로저가 필요 없을 수도 있다. 델리게이트가 없다면 클로저를 전달해서 요청한다."}]}]},{"content":[{"inlineContent":[{"text":"비동기 메서드라서 resume을 보내는 순간 리턴이 되면서 끝난다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이후 응답을 받으면 에러나 혹은 응답받은 결과물을 전달해준다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"리줌은 비동기방식, 콜백이 넘어올 때는 동기방식"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DataTask 예제"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/EwApThz.png","type":"image"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"URLSession이 동작하는 구조"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"데이터 Task를 만들면 Suspended 상태로 시작을 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"리줌을 해주고 나면 Running 상태가 된다."}]}]},{"content":[{"inlineContent":[{"text":"그다음에 받는 이벤트는 Response가 온다. didReceiveResponse 메소드 호출","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"바디가 들어왔으면 didReceiveData 메소드 호출"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"캐시 관련된 리스폰스도 델리게이트 메소드를 호출","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"다 받고나면 didCompleteWithError 메소드가 호출이 되면서 Finished 상태로 바뀐다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"이렇게 시점을 알고싶다면 델리게이트 메소드를 정의해야하고 그렇지 않으면 클로저만 성공했을 때 실패했을 때 클로저만 가지고도 기본적인 동작을 다 구현할 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Background Transfer"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"앱이 멈춘 후에도 백그라운드 전송 지원"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"이벤트 처리를 위한 델리게이트 메소드가 필수"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"HTTP \/ HTTPS 프로토콜만"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"리다이렉트 지원"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"업로드는 파일만 가능","type":"text"}],"type":"paragraph"}]}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[오픈마켓 프로젝트 고민]","type":"text"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"GET에는 바디를 붙일 수가 없다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Header는 여러개일 수도 있다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"상품 삭제의 경우 identifier라는 헤더와 바디에 들어가는 Content-Type 이라는 헤더, 총 2개의 헤더가 들어가야 한다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"Health Checker가 뭐지?"}]}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이거는 API가 살아있는지 확인하는 것"}]}]}],"type":"unorderedList"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"등록, 수정, 삭제 등.. 모두 들어가는 바디가 약간씩 다르다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"각 상황에 맞는 타입을 따로 구현해줘야하려나?","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"http method가 String으로 들어가는데.. 별도 타입으로 만들어주면 좋겠다.","type":"text"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/foundation\/urlsession"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/foundation\/url_loading_system\/fetching_website_data_into_memory","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/youtu.be\/toU4o5Z0cVM","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}],"kind":"content"}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220103_UICollectionView,_HTTP,_URLSession","interfaceLanguage":"swift"},"sections":[],"references":{"https://i.imgur.com/OE5wyTD.png":{"type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/OE5wyTD.png"}],"identifier":"https:\/\/i.imgur.com\/OE5wyTD.png"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/HIaduZp.png":{"type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/HIaduZp.png"}],"identifier":"https:\/\/i.imgur.com\/HIaduZp.png"},"https://i.imgur.com/fGyUKWc.png":{"type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/fGyUKWc.png"}],"identifier":"https:\/\/i.imgur.com\/fGyUKWc.png"},"https://i.imgur.com/EwApThz.png":{"type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/EwApThz.png"}],"identifier":"https:\/\/i.imgur.com\/EwApThz.png"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220104_multipart,_form-data,_aync_method_test,_---_--,_test_doubles.json b/docs/data/documentation/arinote/220104_multipart,_form-data,_aync_method_test,_---_--,_test_doubles.json new file mode 100644 index 0000000..56ea192 --- /dev/null +++ b/docs/data/documentation/arinote/220104_multipart,_form-data,_aync_method_test,_---_--,_test_doubles.json @@ -0,0 +1 @@ +{"schemaVersion":{"patch":0,"major":0,"minor":3},"variants":[{"paths":["\/documentation\/arinote\/220104_multipart,_form-data,_aync_method_test,_---_--,_test_doubles"],"traits":[{"interfaceLanguage":"swift"}]}],"sections":[],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220104_multipart,_form-data,_aync_method_test,_---_--,_Test_Doubles","interfaceLanguage":"swift"},"metadata":{"roleHeading":"Article","role":"article","title":"220104 multipart, form-data, aync method test, 의존성 주입, Test Doubles","modules":[{"name":"Ari's Notes"}]},"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"primaryContentSections":[{"content":[{"anchor":"TIL-Today-I-Learned","type":"heading","text":"TIL (Today I Learned)","level":1},{"inlineContent":[{"text":"1월 4일 (화)","type":"text"}],"type":"paragraph"},{"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"오픈 마켓 프로젝트 STEP 1 진행","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"multipart\/form-data"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"비동기 메소드 테스트 하는 방법","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"의존성 주입이란?"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Test Doubles","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2,"type":"heading","text":"고민한 점 \/ 해결 방법"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[multipart\/form-data]"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"multipart 타입을 통해 MIME은 트리 구조의 메세지 형식을 정의할 수 있다."}]},{"items":[{"content":[{"inlineContent":[{"text":"어떤 것이 첨부된 텍스트(multipart\/mixed)","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"텍스트와 HTML과 같이 다른 포맷을 함께 보낸 메세지(multipart\/alternative)"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"multipart 메세지"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"서로 붙어있는 여러개의 메세지를 포함하여 하나의 복합 메세지로 보내진다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"MIME multipart 메세지는 “Content-Type:” 헤더에 boundary 파라미터를 포함한다"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"boundary는 메세지 파트를 구분하는 역할을 하며 메세지의 시작과 끝 부분도 나타난다."}]}]},{"content":[{"inlineContent":[{"text":"첫번째 Boundary 전에 나오는 내용은 MIME을 지원하지 않는 클라이언트를 위해 제공된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"boundary를 선택하는 것은 클라이언트의 몫이다. 보통 부작위의 문자를 선택해 메세지의 본문과 충돌을 피한다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UUID"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"multipart form 제출","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"HTTP form을 채워서 제출하면 가변 길이 텍스트 필드와 업로드 될 객체는 각각 멀티파트 본문을 구성하는 하나의 파트가 보내진다. 멀티파트 본문은 여러 다른 종류와 길이의 값으로 채워진 form을 허용한다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"multipart\/form-data"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자가 양식을 작성한 결과 값의 집합을 번들로 만드는데 사용한다."}]}]}]}]}],"type":"unorderedList"},{"type":"aside","style":"note","name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"First, there’s the Content-Type header. It contains information about the type of data you’re sending (multipart\/form-data;) and a boundary. This boundary should always have a unique, somewhat random value. In the example above I used a UUID. Since multipart forms are not always sent to the server all at once but rather in chunks, the server needs some way to know when a certain part of the form you’re sending it ends or begins. This is what the boundary value is used for. This must be communicated in the headers since that’s the first thing the receiving server will be able to read."}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"파일 업로드할 때 알아야하는 HTTP 규약"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/allgCmS.png","type":"image"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"서버에 ","type":"text"},{"code":"multipart\/form-data","type":"codeVoice"},{"text":"로 데이터를 보낼 때의 request header와 body는 위 이미지와 같이 구성되어있다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Content-Type이 multipart\/form-data로 지정되어있어야 서버에서 정상적으로 데이터를 처리할 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"전송되는 파일 데이터의 구분자로 boundary에 지정되어 있는 문자열을 이용한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"boundary의 문자열 마지막 "},{"type":"codeVoice","code":"**------WebKitFormBoundaryQGvWeNAiOE4g2VM5--**"},{"type":"text","text":" 값은 다른값과 다르게 "},{"type":"codeVoice","code":"--"},{"type":"text","text":"가 마지막에 붙어있는데, body의 끝을 알리는 의미를 가진다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"이 규격에 맞게 http header와 body 데이터를 생성한 후 HTTP server에 요청하게 되면 서버에서도 HTTP 통신 규격에 맞게 데이터를 파싱한 후 처리하게 된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"아래는 HTTP Request Data, HTTP Response Data 예시다.","type":"text"}],"type":"paragraph"}]}]},{"syntax":null,"type":"codeListing","code":["## HTTP Request Data","","POST \/file\/upload HTTP\/1.1[\\r][\\n]","","Content-Length: 344[\\r][\\n]","","Content-Type: multipart\/form-data; boundary=Uee--r1_eDOWu7FpA0LJdLwCMLJQapQGu[\\r][\\n]","","Host: localhost:8080[\\r][\\n]","","Connection: Keep-Alive[\\r][\\n]","","User-Agent: Apache-HttpClient\/4.3.4 (java 1.5)[\\r][\\n]","","Accept-Encoding: gzip,deflate[\\r][\\n]","","[\\r][\\n]","","--Uee--r1_eDOWu7FpA0LJdLwCMLJQapQGu[\\r][\\n]","","Content-Disposition: form-data; name=files; filename=test.txt[\\r][\\n]","","Content-Type: application\/octet-stream[\\r][\\n]","","[\\r][\\n]","","aaaa","","[\\r][\\n]","","--Uee--r1_eDOWu7FpA0LJdLwCMLJQapQGu[\\r][\\n]","","Content-Disposition: form-data; name=files; filename=test1.txt[\\r][\\n]","","Content-Type: application\/octet-stream[\\r][\\n]","","[\\r][\\n]","","1111","","[\\r][\\n]","","--Uee--r1_eDOWu7FpA0LJdLwCMLJQapQGu--[\\r][\\n]","","","## HTTP Response Data","","HTTPHTTP\/1.1 200 OK[\\r][\\n]","","Server: Apache-Coyote\/1.1[\\r][\\n]","","Accept-Charset: big5, big5-hkscs, euc-jp, euc-kr...[\\r][\\n]","","Content-Type: text\/html;charset=UTF-8[\\r][\\n]","","Content-Length: 7[\\r][\\n]","","Date: Mon, 30 Jun 2014 01:28:19 GMT[\\r][\\n]","","[\\r][\\n]","","SUCCESS"]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"추가적으로 header와 header를 구분하는 것은 개행 문자이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"header와 body를 구분하는 것은 개행문자 2개이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"body에 포함되어있는 file data를 구분하는 것은 boundary이다.","type":"text"}]}]}]},{"type":"aside","content":[{"inlineContent":[{"type":"text","text":"(개행)바운더리문자열(개행)을 기준으로 구분하게 된다. 또 이 때의 개행은 플랫폼에 상관없이 CRLF로 \\r\\n을 사용해야 한다."}],"type":"paragraph"}],"name":"Note","style":"note"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[비동기 메소드 테스트 하기]"}]}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"비동기 메서드는 파생된 스레드에서의 작업을 기다리지 않고 바로 끝나기 때문에, 일반적인 방법으로는 정상적인 테스트가 불가능하다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 파생된 스레드에서의 작업을 기다리게 하는 방법을 알아보자."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"세가지 테스트 메소드를 통해 해결해줄 수 있다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"expectation(description:)"}]},{"items":[{"content":[{"inlineContent":[{"text":"어떤 것이 수행되어야 하는지 문자열로 정해준다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"fulfill()"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정의해둔 expectation이 충족되는 시점에 호출하여 동작을 수행했음을 알린다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"wait(for:timeout:)","type":"codeVoice"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"expectation을 배열로 담아 전달하여 배열 속의 expectation이 모두 "},{"type":"codeVoice","code":"fulfuill"},{"type":"text","text":"될 때 까지 기다린다. timeout을 설정하여 시간을 제한할 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"비동기 작업을 기다리는 제한 시간 같은건데, 만약 비동기 메서드가 끝없이 동작하고 있다면 테스트가 진행되지 않을 것이다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"syntax":"swift","code":["\/\/ 공식문서의 비동기 메소드 테스트 예제코드","func testDownloadWebData() {"," "," \/\/ Create an expectation for a background download task."," let expectation = XCTestExpectation(description: \"Download apple.com home page\")"," "," \/\/ Create a URL for a web page to be downloaded."," let url = URL(string: \"https:\/\/apple.com\")!"," "," \/\/ Create a background task to download the web page."," let dataTask = URLSession.shared.dataTask(with: url) { (data, _, _) in"," "," \/\/ Make sure we downloaded some data."," XCTAssertNotNil(data, \"No data was downloaded.\")"," "," \/\/ Fulfill the expectation to indicate that the background task has finished successfully."," expectation.fulfill()"," "," }"," "," \/\/ Start the download task."," dataTask.resume()"," "," \/\/ Wait until the expectation is fulfilled, with a timeout of 10 seconds."," wait(for: [expectation], timeout: 10.0)","}"],"type":"codeListing"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[의존성 주입]","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"의존성이란?"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"어떤 객체가 내부에서 생성하여 가지고 있는 객체를 의존성이라고 한다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"의존성 주입이란?"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"말 그대로 의존성을 주입시킨다는 뜻. 내부에서 초기화가 이루어지는 것이 아니라 외부에서 객체를 생성하여 내부에 주입해주는 것을 뜻한다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"의존성 주입을 왜 사용하지?"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"객체간의 결합도를 낮추기 위함이다. 객체 간의 결합도가 낮으면 리팩토링이 쉽고 테스트 코드 작성이 쉬워진다는 장점이 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"코드 재사용이 용이하다. 공통적인 로직은 한번만 구현하고 필요한 정보 혹은 기능만 끼워넣어서 커스터마이징이 가능하기 때문에 여러곳에서 재사용이 가능하다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"객체의 책임을 명확히 알 수 있다."}]}]}]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[Test Doubles]"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Dummy","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"객체는 전달되지만 사용되지 않는 객체"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"예를 들면 함수 파라미터에 전달되는 빈 객체등이 있다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Fake","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"동작하는 구현을 가지고 있지만 실제 프로덕션에는 적합하지 않은 객체","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"단순화 된 버전의 동작을 제공하기도 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"예를 들어 "},{"identifier":"https:\/\/martinfowler.com\/bliki\/InMemoryTestDatabase.html","type":"reference","isActive":true},{"type":"text","text":"가 있다."}],"type":"paragraph"}]},{"content":[]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Stub","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"테스트에서 호출된 요청에 대해 미리 준비해둔 결과를 제공한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테스트를 위해 프로그래밍 된 내용 이외에는 응답하지 않는다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"주로 사용되는 경우"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"구현이 되지 않은 함수나, 라이브러리에서 제공하는 함수를 사용하고자 할 때","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"함수가 반환하는 값을 임의로 생성하고 싶을 때","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"복잡한 논리 흐름을 가지는 경우 테스트를 단순화 하고 싶을 때","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"의존성을 가지는 유닛의 응답을 모사하여 독립적인 시험 수행을 하고자 할 때"}]}]}]}]},{"content":[{"inlineContent":[{"text":"얻는 이점","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"의존하는 것에 대하여 독립적으로 개발과 테스트가 가능해진다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"interface만 존재하는 것을 stub으로 개발하고 테스트할 수 있다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"촘촘한 테스트가 가능해진다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"stub으로 다양한 응답결과 케이스를 만들어 테스트할 수 있다.","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Spy","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Stub의 역할을 가지면서 호출된 내용에 대해 약간의 정보를 기록한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테스트에서 확인하기 위한 정보이다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"예를 들면 메일링 서비스에서 발송된 메일의 갯수를 기록"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"함수가 얼마나 많이 호출되었는지, 어떤 인수가 전달되었는지 확인"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Mock"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"호출에 대한 기대를 명세하고 해당 내용에 따라 동작하도록 프로그래밍 된 객체"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테스트 작성을 위한 환경 구축이 어려울 때"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"환경 구축을 위한 작업 시간이 많이 필요한 경우 Mock 객체를 사용"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"특정 모듈을 아직 갖고 있지 않아서 테스트 환경을 구축하지 못할 경우","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"타 부서와의 협의나 정책이 필요한 경우에도 Mock이 필요"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"연계 모듈이라서 다른 쪽에서 승인을 해줘야 테스트가 가능한 경우, 방화벽으로 막혀 있어서 통과가 어려운 경우 등이 이에 속함"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"테스트가 특정 경우나 순간에 의존적일 때"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"테스트 시간이 오래 걸리는 경우"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"일반적인 테스트 더블은 상태를 기반으로 테스트 케이스를 작성한다."},{"type":"text","text":" "},{"text":"Mock 객체는 행위를 기반으로 테스트 케이스를 작성한다.","type":"text"}]}],"name":"Note","style":"note","type":"aside"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/www.donnywals.com\/uploading-images-and-forms-to-a-server-using-urlsession\/"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/soooprmx.com\/multipart-form-data-%ed%83%80%ec%9e%85%ec%9d%98-http-%eb%a9%94%ec%8b%9c%ec%a7%80-%ea%b5%ac%ec%84%b1-%eb%b0%a9%eb%b2%95\/"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/lena-chamna.netlify.app\/post\/http_multipart_form-data\/"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/www.iana.org\/assignments\/media-types\/media-types.xhtml#multipart"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/lena-chamna.netlify.app\/post\/uploading_array_of_images_using_multipart_form-data_in_swift\/","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/xctest\/asynchronous_tests_and_expectations\/testing_asynchronous_operations_with_expectations"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/wody.tistory.com\/10"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/dongminyoon.tistory.com\/55"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@leeyoungwoozz\/Test-Doubles"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/hyunsikwon.github.io\/swift\/Swift-DI-01\/"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/wody.tistory.com\/16"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/zeddios.tistory.com\/1103","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}],"kind":"content"}],"references":{"https://i.imgur.com/allgCmS.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/allgCmS.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/allgCmS.png","type":"image"},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]},"https://martinfowler.com/bliki/InMemoryTestDatabase.html":{"title":"인메모리 데이터 베이스","titleInlineContent":[{"text":"인메모리 데이터 베이스","type":"text"}],"url":"https:\/\/martinfowler.com\/bliki\/InMemoryTestDatabase.html","identifier":"https:\/\/martinfowler.com\/bliki\/InMemoryTestDatabase.html","type":"link"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220105_urlprotocolmock,_wwdc2018,_urlsession,_networktest.json b/docs/data/documentation/arinote/220105_urlprotocolmock,_wwdc2018,_urlsession,_networktest.json new file mode 100644 index 0000000..cecfd05 --- /dev/null +++ b/docs/data/documentation/arinote/220105_urlprotocolmock,_wwdc2018,_urlsession,_networktest.json @@ -0,0 +1 @@ +{"kind":"article","primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","type":"heading","level":1,"text":"TIL (Today I Learned)"},{"inlineContent":[{"text":"1월 5일 (수)","type":"text"}],"type":"paragraph"},{"text":"학습 내용","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"오픈 마켓 프로젝트 STEP 1 테스트 진행"}]},{"items":[{"content":[{"inlineContent":[{"text":"URLProtocol을 Mock으로 만들어서 네트워크 테스트 진행하기","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","type":"heading","level":2},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[URLProtocol?]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"야곰닷넷의 Unit Test강의를 참고하여 테스트코드를 위한 Mock 객체를 구현하다가 마주하게 된 경고"}],"type":"paragraph"}]}]},{"syntax":"swift","code":["class StubURLSessionDataTask: URLSessionDataTask {"," var dummyData: DummyData?",""," \/\/ init 부분에서 에러가 났다."," init(dummy: DummyData?, completionHandler: DataTaskCompletionHandler?) {"," self.dummyData = dummy"," self.dummyData?.completionHandler = completionHandler"," }",""," override func resume() {"," dummyData?.completion()"," }","}"],"type":"codeListing"},{"content":[{"type":"paragraph","inlineContent":[{"code":"'init()' was deprecated in iOS 13.0: Please use -[NSURLSession dataTaskWithRequest:] or other NSURLSession methods to create instances","type":"codeVoice"}]}],"name":"Note","type":"aside","style":"note"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"해당 경고를 없애고 싶어 구글링 중에 ","type":"text"},{"code":"URLProtocol","type":"codeVoice"},{"text":"을 발견하게 되었다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/forums.raywenderlich.com\/t\/chapter-8-init-deprecated-in-ios-13\/102050\/7"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"코드만으로 이해가 어려워서 ","type":"text"},{"identifier":"https:\/\/developer.apple.com\/videos\/play\/wwdc2018\/417\/","type":"reference","isActive":true},{"type":"text","text":" 부분을 공부해보았다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"요약"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Testing Network Requests"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Decompose code for testability"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"URLProtocol as a mocking tool"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Tiered testing strategy"}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"inlineContent":[{"type":"reference","isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/urlprotocol"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/YGO4AZM.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"URLSession은 네트워크 통신을 할 수 있는 high level API를 제공한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"반면에 URLProtocol이라는 low level API도 제공한다."}]},{"items":[{"content":[{"inlineContent":[{"text":"네트워크 연결을 열고, request를 쓰고, respnse를 주는 역할이다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"URLProtocol은 단독으로 쓰이지 않고 상속을 받도록 설계되어져있다. URL 로딩 시스템을 확장할 수 있게 한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"URLProtocol을 사용해서 Mock객체를 만들고 Mock response를 만들어서 테스트를 진행할 수 있다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"테스트 피라미드"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Unit Test","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"한개의 메소드나 클래스를 테스트하는 가장 작은 단위이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"메소드의 성공, 실패등을 테스트한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"1분당 수백, 수천개의 테스트를 진행할 수 있다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Integration"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"한 개의 기능이 잘 작동하는 지를 테스트 하는 것"}]}]},{"content":[{"inlineContent":[{"text":"여러 계층으로 연결된 클래스나 메소드가 잘 동작하는지 확인한다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"End-to-end"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"하나의 프로그램이나 앱이 잘 동작하는지를 테스트 하는 것이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"사용자가 기기에서 수행하는 동작들을 테스트 한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"주로 UI Test로 진행한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"앱 내부 뿐만 아니라 OS나 외부 리소스와도 잘 상호작용 하는지 확인한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/ffJ16Vh.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"URLRequest를 준비하고"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"task가 서버와 통신을 하고","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"통신한 데이터를 파싱하고"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"파싱한 데이터를 뷰에 뿌려준다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[공부하다 메모한 흔적]"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테스트 기법중 하나를 메모…"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"네트워크나 혹은 여러 side-effact에 강하게 커플링이 되어있는 객체를 가지고 테스트를 할 때에는 외부에서 가짜 데이터를 제공하는 어떤 Stub, 혹은 Mock 객체를 넣고 그 동작을 테스트 할 수 있는 것"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"resume()은 왜 호출해주는 걸까?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"dataTask는 기본적으로 일시정지(suspended)상태로 시작하기 때문이다. dataTask를 시작해주기 위해 resume()을 호출하는 것이다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"몰랐는데… URLRequest의 HTTPMethod의 기본값은 GET이였다."}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[이해를 위해 프로젝트에서 작성한 코드 설명해보기]"}],"type":"strong"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"MockURLProtocol"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"URL 데이터 로딩을 다루는 추상클래스"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"URLProtocol은 URLProtocolClient 프로토콜을 통해 네트워크 진행 상황을 전달한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"테스트 번들에서 MockURLProtocol 클래스를 만들고 메소드를 재정의 해준다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"로드를 할 때 설정한 후 전달할 Data, Error, Response를 딕셔너리로 설정해준다."}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이 값은 URLProtocol에 연결하여 설정값을 세팅해주기 위한 값이 된다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Unit Test를 위해 상속받아서 오버라이드 함으로써 커스텀 하여 Mock 객체를 새롭게 만들 수 있다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"기존처럼 외부 네트워크에 요청을 직접 보내는 동작이 아니라, 요청을 가로채서 원하는 응답을 반환하게 끔 커스텀 하는 작업이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"즉 원래 같이 웹 서버에서 데이터를 불러오는 과정이 아니고, 내가 설정한 값(data, response)을 그대로 반환하게 만들어 주는 과정인 것이다."}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"MockSession"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앞서 만든 프로토콜을 URLSession의 configuration에 넣어주면 개발자가 임의로 작성한 로직이 URLSession 내부에서 실행될 것이다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"NetworkManager 테스트코드","type":"codeVoice"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테스트코드를 작성할 때에는 원하는 결과 값(url, data, response)을 mockURLs 딕셔너리 값에 대입해주고 그걸 토대로 성공, 혹은 실패에 대한 네트워크 테스트를 진행한다."}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/videos\/play\/wwdc2018\/417\/","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/foundation\/urlprotocol"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/velog.io\/@seunkim\/WWDC18-Testing-Tips-Tricks1-Testing-network-requests","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@leeyoungwoozz\/TIL-2021.05.21-Fri"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@minni\/URLSession-URLSessionConfiguration-URLSessionTask"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/www.youtube.com\/watch?v=meTnd09Pf_M&t=229s","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/forums.raywenderlich.com\/t\/chapter-8-init-deprecated-in-ios-13\/102050\/7"}],"type":"paragraph"}]}]}]}]}]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220105_URLProtocolMock,_WWDC2018,_URLSession,_NetworkTest","interfaceLanguage":"swift"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"metadata":{"title":"220105 URLProtocolMock, WWDC2018, URLSession, NetworkTest","roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}]},"schemaVersion":{"minor":3,"patch":0,"major":0},"sections":[],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220105_urlprotocolmock,_wwdc2018,_urlsession,_networktest"]}],"references":{"https://i.imgur.com/ffJ16Vh.png":{"alt":null,"identifier":"https:\/\/i.imgur.com\/ffJ16Vh.png","variants":[{"url":"https:\/\/i.imgur.com\/ffJ16Vh.png","traits":["1x","light"]}],"type":"image"},"https://developer.apple.com/documentation/foundation/urlprotocol":{"identifier":"https:\/\/developer.apple.com\/documentation\/foundation\/urlprotocol","title":"How to Use URLProtocol","type":"link","titleInlineContent":[{"type":"text","text":"How to Use URLProtocol"}],"url":"https:\/\/developer.apple.com\/documentation\/foundation\/urlprotocol"},"https://developer.apple.com/videos/play/wwdc2018/417/":{"identifier":"https:\/\/developer.apple.com\/videos\/play\/wwdc2018\/417\/","title":"WWDC 2018 Testing Tips & Tricks","type":"link","titleInlineContent":[{"text":"WWDC 2018 Testing Tips & Tricks","type":"text"}],"url":"https:\/\/developer.apple.com\/videos\/play\/wwdc2018\/417\/"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://i.imgur.com/YGO4AZM.png":{"alt":null,"identifier":"https:\/\/i.imgur.com\/YGO4AZM.png","variants":[{"url":"https:\/\/i.imgur.com\/YGO4AZM.png","traits":["1x","light"]}],"type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220106_uisegmentedcontrol,_----pr1---.json b/docs/data/documentation/arinote/220106_uisegmentedcontrol,_----pr1---.json new file mode 100644 index 0000000..d36bf57 --- /dev/null +++ b/docs/data/documentation/arinote/220106_uisegmentedcontrol,_----pr1---.json @@ -0,0 +1 @@ +{"sections":[],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220106_uisegmentedcontrol,_----pr1---"]}],"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"schemaVersion":{"minor":3,"major":0,"patch":0},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220106_UISegmentedControl,_----PR1---","interfaceLanguage":"swift"},"primaryContentSections":[{"content":[{"type":"heading","anchor":"TIL-Today-I-Learned","level":1,"text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"type":"text","text":"1월 6일 (목)"}]},{"text":"학습 내용","type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"오픈 마켓 프로젝트 STEP 1 PR 작성, README 작성"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"이후 저녁엔 피드백 코멘트 달기","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UISegmentedControl를 알아보기"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UICollectionView로 List 만드는 튜토리얼 따라해보기"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","type":"heading","level":2},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[피드백 받으면서 얻은 꿀팁]"}]}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SwiftLint Rule"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Supports autocorrection"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스네이크 케이스에서 단순히 카멜케이스로 바꿔주는 코딩키 전략"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"KeyDecodingStrategy, KeyEncodingStrategy","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Apple에서 만든 SwiftFormat","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/github.com\/apple\/swift-format"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"SwiftLint를 사용할 때 Pods도 보통 excluded 처리해주곤 한다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"나중에 외부라이브러리를 사용하게 된다면 활용해보자!","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"코드 작성시 한 라인에 워딩수를 제한할 때 메서드명과 파라미터가 길어진다면?","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/google.github.io\/swift\/#function-declarations"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"아래처럼 해주는 이유는 개발자들은 가로를 보는것보다 세로를 보는 것이 더 편하기 때문이다. 화면 해상도도 다 다르고… 등등…"}]}]}],"type":"unorderedList"}]}]},{"type":"codeListing","code":["public func performanceTrackingIndex("," of element: Element,"," in collection: Elements",") -> ( \/\/ 내려쓰기를 이렇게하네 허허~"," Element.Index?,"," PerformanceTrackingIndexStatistics.Timings,"," PerformanceTrackingIndexStatistics.SpaceUsed",") {"," \/\/ ...","}"],"syntax":"swift"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"나중에가면 네트워크 테스트시 실제 사용자 입장에서 테스트해야하기 때문에 fake 테스트를 활용한다고 한다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"예를들어 결졔화면을 테스트해야한다면 실제 네트워크를 사용해서 결제하는척 테스트하고 마지막에 결제는 되지않는 fake 코드를 같이 짠다고 한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/blog.pragmatists.com\/test-doubles-fakes-mocks-and-stubs-1a7491dfa3da?gi=e7d32c619169","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[세그먼트 컨트롤을 네비게이션 바 아이템으로 올리기]"}],"type":"strong"}]},{"type":"codeListing","code":["\/\/ UISegmentedControl의 글꼴 색상 변경하기 (selected, normal)","let normal: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.white]","let selected: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.systemBlue]","control.setTitleTextAttributes(normal, for: .normal)","control.setTitleTextAttributes(selected, for: .selected)","","","\/* 코드로 세그먼트 컨트롤 커스텀 해보기 *\/","\/\/ 커스텀 세그먼트 컨트롤 코드","class CustomeSegmentedControl: UISegmentedControl {"," override func layoutSubviews() {"," super.layoutSubviews()"," layer.cornerRadius = 4 \/\/ 테두리를 둥글게 해줌"," layer.borderWidth = 1 \/\/ 배경의 너비?"," layer.borderColor = UIColor.white.cgColor \/\/ 배경 색깔"," layer.masksToBounds = true"," }","}","\/\/ 뷰컨 내부에 프로퍼티로 추가 (코드로 세그먼트 컨트롤 설정하기)","lazy var segmentedControl: CustomeSegmentedControl = {"," let items = [\"LIST\", \"GRID\"]"," let control = CustomeSegmentedControl(items: items)"," let normal: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.white]"," let selected: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.systemBlue]"," control.selectedSegmentIndex = 0"," control.setTitleTextAttributes(normal, for: .normal)"," control.setTitleTextAttributes(selected, for: .selected)"," control.tintColor = .white"," control.backgroundColor = .systemBlue"," return control","}()","","\/\/ ...","override func viewDidLoad() {"," super.viewDidLoad()","","\t\t\/\/ 네비게이션 아이템에 세그먼트 컨트롤 추가"," self.navigationItem.titleView = segmentedControl","","}"],"syntax":"swift"},{"items":[{"content":[{"inlineContent":[{"text":"느낀점","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"음… 세그먼트 디자인때문에 이렇게까지 해야할까…."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이게 좋아진 버전이라던데…","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그린한태 이야기해봤는데, 프로젝트와 꼭 동일한 디자인을 가질 필요는 없으니 너무 디테일하게 똑같이 안따라해도 된다고 말씀해주셨다. 좀 편안…"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"text":"[세그먼트 컨트롤을 활용하여 뷰 전환하기]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/n6XhuJC.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"네비게이션 컨트롤러를 embed in 해준다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"네비게이션 바에 세그먼트 컨트롤러를 올려준다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그리고 뷰에 컨테이너 뷰를 올리게 되면 위 사진과 같이 뷰컨트롤러가 자동으로 생기면서 세그가 연결되는데, 두개를 연결해준다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"노란색, 파란색 뷰컨에는 UIView가 올라가있는데, 이것을 IBOutlet으로 연결시켜준다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"그리고 세그먼트에 IBAction을 연결하여 선택된 인덱스에 따라 isHidden을 활용해서 한쪽을 숨겨주면 완성.","type":"text"}],"type":"paragraph"}]}]}]}]},{"syntax":"swift","code":["class ViewController: UIViewController {"," @IBOutlet var button: UISegmentedControl! {"," didSet {"," let normal: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.white]"," let selected: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.systemBlue]"," button.setTitleTextAttributes(normal, for: .normal)"," button.setTitleTextAttributes(selected, for: .selected)"," }"," }"," @IBOutlet var listView: UIView!"," @IBOutlet var gridView: UIView!"," "," override func viewDidLoad() {"," super.viewDidLoad()",""," gridView.isHidden = true"," }"," "," @IBAction func switchViews(_ sender: UISegmentedControl) {"," if sender.selectedSegmentIndex == 0 {"," listView.isHidden = false"," gridView.isHidden = true"," } else {"," listView.isHidden = true"," gridView.isHidden = false"," }"," }","}"],"type":"codeListing"},{"items":[{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/rFr9XHX.gif"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이후 콜렉션뷰 튜토리얼을 공부하다가 피드백을 받고 뻗어잤다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/youtu.be\/A6vxDDAUj2o","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/stackoverflow.com\/questions\/63585391\/uisegmentedcontrol-corner-radius-not-changing"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/swiftsenpai.com\/development\/uicollectionview-list-basic\/","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/www.raywenderlich.com\/16906182-ios-14-tutorial-uicollectionview-list","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/foundation\/jsondecoder\/2949119-keydecodingstrategy"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/foundation\/jsonencoder\/2949141-keyencodingstrategy\/"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/github.com\/apple\/swift-format"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}],"kind":"content"}],"metadata":{"modules":[{"name":"Ari's Notes"}],"role":"article","title":"220106 UISegmentedControl, 오픈마켓PR1피드백","roleHeading":"Article"},"references":{"https://i.imgur.com/rFr9XHX.gif":{"identifier":"https:\/\/i.imgur.com\/rFr9XHX.gif","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/rFr9XHX.gif","traits":["1x","light"]}],"type":"image"},"https://i.imgur.com/n6XhuJC.png":{"identifier":"https:\/\/i.imgur.com\/n6XhuJC.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/n6XhuJC.png","traits":["1x","light"]}],"type":"image"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220107_tableview,_collectionviewlistcell,_contentoffset,_contentinset.json b/docs/data/documentation/arinote/220107_tableview,_collectionviewlistcell,_contentoffset,_contentinset.json new file mode 100644 index 0000000..1381b1d --- /dev/null +++ b/docs/data/documentation/arinote/220107_tableview,_collectionviewlistcell,_contentoffset,_contentinset.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","type":"heading","level":1,"anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"type":"text","text":"1월 7일 (금)"}]},{"level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용","type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"오픈 마켓 프로젝트 STEP 2"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UICollectionView로 List를 커스텀할 수 있나…"}]}]}]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"level":2,"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이번 프로젝트 때 리스트 뷰를 컬렉션뷰로 구현해야하나 고민이 많이 되었다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"일단 테이블뷰에서는 설정할 수 있는 부분이 한정되어있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 확장 가능성이 있다면 컬렉션뷰로 구현해두는 것이 더 유연하다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하지만 목록만 보여주는 뷰라면 테이블뷰로 구현하는 것이 간편할 것이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컬렉션뷰로도 리스트 구현이 가능하지만, 테이블뷰로 구현하는 것보다 해줘야할 설정이 많고, 복잡하다."}]}]},{"content":[{"inlineContent":[{"text":"또한 요즘에는 SwiftUI가 점점 선호되는[?] 추세라고 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"그렇지만.. 제리랑 같이 발전된 컬렉션뷰(14.0버전)에 대해 알아보기 위해 삽질했던 시간이 아까워서 각자 한번 다시 도전해보기로 했고, 그래도 힘들다면 테이블뷰로 간편하게 구현하기로 하였다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"컬렉션뷰로 둘다 구현한다면 셀만 갈아끼워서 간단히 구현할 수 있지 않나.. 라는 생각이 들기도 했다.","type":"text"}]}]}]}]}]}]}]},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[WWDC 2020]","type":"text"}]}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"setNeedsUpdateConfiguration()"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"현재 Configuration 업데이트를 셀에 요청하는 메소드. 이 메소드는 자동으로 호출된다."}]}]},{"content":[{"inlineContent":[{"text":"셀이 ConfigurationState가 변경되었을 때","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Collection View List Configuration]"}]}]},{"items":[{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/lnyFWIz.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"콜렉션뷰에서 list를 작성하기 위해 레이아웃 측에서 필요한 3가지 유형","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UICollectionLayoutListConfiguration"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"NSCollectionLayoutSection","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UICollectionViewCompositionalLayout"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UICollectionLayoutListConfiguration은 NSCollectionLayoutSection과 iOS 13에서 도입한 기존 구성 레이아웃 시스템의 일부인 UICollectionViewCompositionalLayout 위에 구축되었다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"iOS 14에서는 UICollectionLayoutListConfiguration이라는 새로운 타입을 제공한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"List Configuration은 테이블뷰의 스타일 (.plain, .grouped, .insetGrouped)과 같은 모양을 제공한다."}]}]},{"content":[{"inlineContent":[{"text":"또한 콜렉션 뷰의 List 전용 .sidebar, .sidebarPlain라는 새로운 스타일을 사용하여 iPadOS 14에서 다중열 앱을 구축할 수 있다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Simple setup]"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/2GMThBg.png"}]}]},{"content":[{"inlineContent":[{"text":"list를 작성하는 가장 쉬운 방법은 UICollectionLayoutListConfiguration을 작성하고 appearance를 제공한 다음 이 구성을 사용하여 UICollectionViewCompositionaLayout을 작성하는 것이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"섹션 별 설정"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/E6nN9ss.jpg"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"구성은 동일하지만 이 구성을 사용하여 compositional layout 대신에 NSCollectionLayoutSection을 작성한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이 코드는 컴포지션 레이아웃의 기존 섹션 공급자 이니셜라이저 내에서 사용될 수 있으며, 컬렉션뷰의 모든 섹션에 대해 호출되어 특정 섹션에 고유한 개별 레이아웃 정의를 반환할 수 있다.","type":"text"}]}]}]},{"inlineContent":[{"inlineContent":[{"text":"[ContentOffset vs ContentInset]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ContentOffset"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"point이다. x, y 좌표를 의미하는 것으로 스크롤 한다는 것 자체가 ContentOffset이 변한다는 의미이다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ContentInset"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컨텐츠에 상하 좌우 여백을 주는 것이다."}]}]},{"content":[{"inlineContent":[{"text":"컨텐츠의 바깥쪽이 아닌 안쪽 여백을 말하는 것이다.","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"https:\/\/jinshine.github.io\/2018\/12\/06\/iOS\/ContentOffset%EA%B3%BC%20ContentInset\/","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/www.raywenderlich.com\/16906182-ios-14-tutorial-uicollectionview-list"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/shoveller.tistory.com\/entry\/WWDC20-Lists-in-UICollectionView"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/videos\/all-videos\/?q=collectionView"}],"type":"paragraph"}]}]}]}]}]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"metadata":{"roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}],"title":"220107 TableView, CollectionViewListCell, ContentOffset, ContentInset"},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220107_TableView,_CollectionViewListCell,_ContentOffset,_ContentInset"},"schemaVersion":{"minor":3,"patch":0,"major":0},"variants":[{"paths":["\/documentation\/arinote\/220107_tableview,_collectionviewlistcell,_contentoffset,_contentinset"],"traits":[{"interfaceLanguage":"swift"}]}],"sections":[],"kind":"article","references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/2GMThBg.png":{"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/2GMThBg.png","traits":["1x","light"]}],"alt":null,"identifier":"https:\/\/i.imgur.com\/2GMThBg.png"},"https://i.imgur.com/lnyFWIz.png":{"identifier":"https:\/\/i.imgur.com\/lnyFWIz.png","variants":[{"url":"https:\/\/i.imgur.com\/lnyFWIz.png","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/E6nN9ss.jpg":{"identifier":"https:\/\/i.imgur.com\/E6nN9ss.jpg","variants":[{"url":"https:\/\/i.imgur.com\/E6nN9ss.jpg","traits":["1x","light"]}],"alt":null,"type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220110_filemanager,_ios_file_system,_multipart_form-data.json b/docs/data/documentation/arinote/220110_filemanager,_ios_file_system,_multipart_form-data.json new file mode 100644 index 0000000..63aef85 --- /dev/null +++ b/docs/data/documentation/arinote/220110_filemanager,_ios_file_system,_multipart_form-data.json @@ -0,0 +1 @@ +{"kind":"article","sections":[],"variants":[{"paths":["\/documentation\/arinote\/220110_filemanager,_ios_file_system,_multipart_form-data"],"traits":[{"interfaceLanguage":"swift"}]}],"primaryContentSections":[{"content":[{"text":"TIL (Today I Learned)","level":1,"type":"heading","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"type":"text","text":"1월 10일 (월)"}]},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"type":"heading","text":"학습 내용"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"오픈 마켓 프로젝트 STEP 2"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Multipart\/form-data 때문에 9시간 삽질..","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"text":"고민한 점 \/ 해결 방법","level":2,"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[네트워킹 할 때 dataTask 함수 내부에서 상태코드와 응답메세지 확인하는 방법]","type":"text"}],"type":"strong"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뭔가 상태코드가 에러가 났을 때 리스폰스에 상태메세지가 담기는 줄 알았는데, data에 json 형식으로 담아주더라… 일단 이거 알아내는데 삽질.."}]}]}],"type":"unorderedList"},{"code":["\/\/ dataTask 내부. resoponse 바인딩 처리 이후...","guard (200...299).contains(response.statusCode) else {"," completion(.failure(NetworkError.statusCode(response.statusCode))) \/\/ 에러 처리"," print(\"---\")"," print(httpResponse.statusCode) \/\/ 상태코드 출력",""," if let convertedJsonIntoDict = try? JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {"," \/\/ 응답메세지를 data > Json > NSDictionary로 변환해서 출력"," print(convertedJsonIntoDict)"," }",""," print(\"---\")"," return \/\/ 클로저 리턴","}"],"syntax":"swift","type":"codeListing"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그래서 위에 data를 JSONSerialization로 json으로 변환 후 NSDictionary로 다운캐스팅 해주면 상태메세지가 아래처럼 나온다."}]}]},{"content":[{"inlineContent":[{"text":"아래 메세지는 삽질하다가 경험했던 코드랑 메세지중 하나.. 406도 보았다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"codeListing","syntax":null,"code":[" {"," code = 500;"," message = \"Cannot invoke \\\"java.awt.image.BufferedImage.getWidth()\\\" because \\\"buffer\\\" is null\";"," }"]},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[URLRequest에 등록한 헤더, 바디를 확인하는 방법]"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"헤더는 아래 프로퍼티에 접근해서 print를 찍으면 출력해주더라"}]},{"items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"request.allHTTPHeaderFields"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"Request의 바디 내부 문자열은 String 내부 init을 활용했다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"String(data: request.httpBody!, encoding: .utf8)"}]}]}]}]}]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[삽질 기록]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"삽질하고 나면 뿌듯함보다 현타가 씨게 찾아온다… 정상인가…","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"시작은 다른 팀들이 테스트로 상품등록을 한걸 보고 우리 코드로도 상품 등록이 되나 확인차 테스트를 한게 시작이였다. 그게 저녁 9시쯤이였나…"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"근데 제대로 되질 않았다. 상태코드, 응답메세지, multipart form-data 양식을 꼼꼼히 살펴보면서 삽질하기 시작했다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"406 에러가 나서 헤더가 문제인 줄 알았는데, 그건 아니였다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"결국 문제는 하드코딩을 개선하려고 만들어 두었던 타입에서 연관값을 활용해 Data 타입과 문자열을 함께 String 타입으로 반환하는 것이 문제였다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"예를 들면 이런식.. "},{"type":"codeVoice","code":"\"\\(data)\\r\\n\""}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그래서 문자열은 문자열대로 append 시키고, Data타입은 또 따로.. 각 타입별로 append를 분리해주니 해결되었다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"편의를 위해 만들었던 "},{"type":"codeVoice","code":"append(_ string:)"},{"type":"text","text":"를 잘못써먹고 있었네…"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"해결하고나니 프로젝트 코드의 문제점들이 꽤나 많았었다. 리팩토링을 많이 해야할 것 같다."}],"type":"paragraph"}]}]},{"code":["body.append(\"--\\(boundary)\\r\\n\")","body.append(\"Content-Disposition: form-data; name=\\\"params\\\"\\r\\n\")","body.append(\"Content-Type: application\/json\\r\\n\\r\\n\")","body.append(encodeData) \/\/ 앤 데이터라 따로 append","body.append(\"\\r\\n\")"],"type":"codeListing","syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Mock 테스트할 땐 문제 없었는데, 실제로 API를 찌르는 테스트를 하다보니 발견한 문제점들이다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그린 말대로 fake 테스트도 꼭 필요할 것 같다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"다 마치고나니 오전 6시였다. 3시쯤 해결못하는 나자신한태 빡쳐서 자려고 누웠었는데, 찝찝함에 잠이 오질 않았다. 결국엔 다 해결하고 쪽잠 3시간을 잤네 😇"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[iOS File System]","type":"text"}]}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"파일\/자료를 탐색\/접근\/보관 등의 기능으로 파일을 효율적으로 관리하는 시스템"},{"text":" ","type":"text"},{"type":"text","text":"* 영구 저장소 관리"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"OS <-> File System <-> Disk"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"애플 파일 시스템"}]},{"items":[{"content":[{"inlineContent":[{"text":"Apple file system, APFS로 불리며 애플에서 macOS, iOS, watchOS, tvOS 모두에서 범용으로 사용하도록 만든 파일시스템","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"디스크 파일 시스템(디스크 드라이브 관리)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"기존 HFS+ 대체하는 신규 파일 시스템","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"iOS에서는 10.3 버전부터 macOS에서는 하이 시에라 버전부터 도입"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"애플 파일 시스템의 기능"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컨테이너 방법 사용하여 컨테이너 내 파티션 용량 조전"}]}]},{"content":[{"inlineContent":[{"text":"카피 온 라이트 지원 -> 스냅샷 용이","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"강력한 암호화 기능 추가"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/XNuBm1z.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"iOS file system","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"iOS 파일 시스템은 해당 각각의 앱에 맞춰져있다. (시스템 간결성 및 보안 측면)"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"앱 SandBox 디렉토리 내부에서 파일 시스템과 상호작용 (앱 설치시 각 기능의 컨테이너를 SandBox에 담음)","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"budle Container: 앱 번들"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Data Container: 앱\/유저 데이터 해당 컨테이너 안에 서브 디렉토리가 생성되어 데이터를 관리","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"iCloud Container: iCloud 관련 데이터"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"다양한 디렉토리","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Bundle(MyApp.app)","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"이 디렉토리는 앱과 모든 리소스를 포함한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"쓰기가 불가능하다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"변조를 방지하기 위해 bundle 디렉토리는 app 설치시 서명된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이 디렉토리에서 쓰기를 하게 되면 서명이 변경되고, app이 launch되지 않는다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"app bundle에 저장된 모든 리소스에 대한 읽기 전용 접근 권한을 얻을 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"iCloud \/ iTunes 백업불가"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"iTunes는 App Store에서 구입한 모든 응용 프로그램의 초기 동기화를 수행한다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Documents"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자 생성 콘텐츠 저장"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"파일 공유를 통해 사용자가 사용할 수 있다. 따라서 사용자에게 공개할 수 있는 파일만 포함되어야 한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"사용자가 수정\/추가\/삭제 가능(iTunes 파일 공유)","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자에게 노출 가능"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"iCloud \/ iTunes 백업","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Inbox"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"외부 애플리케이션 전달 자료 저장"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"iCloud \/ iTunes 백업"}]}]}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Documents\/Inbox"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"앱이 외부 엔티티에서 열도록 요청한 파일에 접근한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"특이 메일은 앱과 관련된 이메일 첨부 파일을 inbox에 저장한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Document interaction controllers는 파일을 파일 app에 배치할 수도 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"다른 앱을 통해 전송받은 파일이 저장되는 디렉토리"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"파일을 읽거나 삭제는 가능하지만 추가 및 수정은 안된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자가 이 디렉토리의 파일을 편집하려고 하면 변경하기 전에 디렉토리에서 앱을 이동해야한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"유저가 직접 생성 불가"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"iCloud \/ iTunes 백업 가능","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Library"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"사용자 데이터 파일이 아닌 파일의 최상위 디렉토리","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"여러 표준 하위 디렉토리 중 하나에 파일을 저장한다","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"iOS app은 일반적으로 Application support와 Caches의 하위 디렉토리를 사용"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자 지정 하위 디렉토리 생성 가능"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"사용자에게 노출시키지 않으려는 파일에는 라이브러리 서브 디렉토리를 사용해야한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"앱에서 사용자 데이터 파일용으로 이러한 디렉토리를 사용하지 않아야 한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"라이브러리 디렉토리의 Contents (Caches 서브 디렉토리 제외)는 iCloud \/ iTunes에 의해 백업된다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Library\/Application Support"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"앱의 기능 관리를 위해 지속적으로 관리해주어야하는 파일을 저장하는 경로","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"CoreData 기본 저장 경로"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"핸드폰 기기 내에 앱 폴더(sandbox)가 생기고, 사용자는 Application Support는 열어볼 수 없다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Document는 앱에서 허용했을 경우에만 접근 가능하다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"당연히 개발자 입장에서도 Application Support에도 접근 가능하다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Library\/Caches"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱의 동작 속도, 데이터 절약 등을 위해 사용하는 공간"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"쉽게 재생성하고 다운로드 할 수 있는 파일만 저장","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"디스크 공간이 부족하거나 시스템 복원의 경우 시스템에 의해 자동으로 파일이 삭제될 수 있는 디렉토리","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"삭제되어도 무방한 파일만 저장하자."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"iCloud \/ iTunes 백업 불가"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"tmp","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱 실행 사이에 유지할 필요가 없는 임시파일을 저장하는 곳"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"더이상 필요하지 않으면 앱에서 이 디렉토리의 파일을 삭제해야 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱이 실행되고 있지 않을 때도 시스템에서 이 디렉토리를 제거할 수 있다."}]}]},{"content":[{"inlineContent":[{"text":"iCloud \/ iTunes 백업 불가","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[FileManager]","type":"text"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"문자열을 담은 .txt 파일을 생성"}],"type":"paragraph"}]}]},{"syntax":"swift","type":"codeListing","code":["let fileManager = FileManager.default","","\/\/ 디렉토리 경로","let applicationSupportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]","let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]","let tmpURL = fileManager.temporaryDirectory","","\/\/ 생성할 파일 경로","let fileURL = tmpURL.appendingPathComponent(\"abc.txt\")","","\/\/ 삽입할 문자열","let text = NSString(string: \"Hello, Files!\")","","do {"," try text.write(to: fileURL, atomically: true, encoding: String.Encoding.utf8.rawValue)"," print(fileURL)","} catch {"," print(\"실패\")","}"]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"파일을 읽어서 출력하기"}],"type":"paragraph"}]}]},{"syntax":"swift","type":"codeListing","code":["do {"," let dataFromPath: Data = try Data(contentsOf: fileURL) \/\/ URL을 불러와서 Data타입으로 초기화"," let text: String = String(data: dataFromPath, encoding: .utf8) ?? \"문서없음\" \/\/ Data to String"," print(text) \/\/ 출력","} catch let e {"," print(e.localizedDescription)","}"]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"파일을 삭제하기"}],"type":"paragraph"}]}],"type":"unorderedList"},{"syntax":"swift","code":["do {"," try fileManager.removeItem(at: fileURL) \/\/ 삭제할 파일의 경로"," print(fileURL)","} catch {"," print(\"실패\")","}"],"type":"codeListing"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"https:\/\/jinshine.github.io\/2018\/12\/06\/iOS\/ContentOffset%EA%B3%BC%20ContentInset\/","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/www.raywenderlich.com\/16906182-ios-14-tutorial-uicollectionview-list","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/shoveller.tistory.com\/entry\/WWDC20-Lists-in-UICollectionView"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/videos\/all-videos\/?q=collectionView","type":"text"}]}]}],"type":"unorderedList"}]}]}],"kind":"content"}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220110_FileManager,_iOS_File_System,_Multipart_form-data","interfaceLanguage":"swift"},"schemaVersion":{"major":0,"minor":3,"patch":0},"metadata":{"title":"220110 FileManager, iOS File System, Multipart\/form-data","modules":[{"name":"Ari's Notes"}],"role":"article","roleHeading":"Article"},"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/XNuBm1z.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/XNuBm1z.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/XNuBm1z.png"}],"alt":null},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220110_nscache.json b/docs/data/documentation/arinote/220110_nscache.json new file mode 100644 index 0000000..3b503ef --- /dev/null +++ b/docs/data/documentation/arinote/220110_nscache.json @@ -0,0 +1 @@ +{"schemaVersion":{"patch":0,"minor":3,"major":0},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220110_NSCache"},"sections":[],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"kind":"article","primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","level":1,"type":"heading","text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"text":"1월 11일 (화)","type":"text"}]},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용","type":"heading","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"오픈 마켓 프로젝트 STEP 2 PR 작성"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"NSCache이란?"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"text":"고민한 점 \/ 해결 방법","level":2,"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[NSCache]","type":"text"}],"type":"strong"}]},{"items":[{"content":[{"inlineContent":[{"text":"캐시메모리(Cache Memory)란?","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"캐시 메모리는 메인 메모리와 CPU간의 데이터 속도 향상을 위한 중간 버퍼 역할을 하는 CPU 내 또는 외에 존재하는 메모리","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"실제 메모리와 CPU 사이에서 빠르게 전달하기 위하여 프로세서가 단기간에 필요로 할 가능성이 높은 정보를 임시로 저장할 목적으로 사용한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"일반적으로 용량이 더 큰 저장장치들은 용량이 작은 저장장치들보다 속도가 느린데 속도차이에 따른 병목현상을 줄이기 위한 범용 메모리이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"지역성을 이용하여 CPU가 어떤 데이터를 원할 것인가를 어느정도 예측하여 캐시메모리에 데이터를 저장한다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"지역성(Locality)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"CPU에서 명령어를 수행하면서 매번 Cache Memory를 참조하게 되는데 이때 Hit률이 지역성을 갖는다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"지역성(Locality Of Reference)은 프로세스들이 기억장치 내의 정보를 균일하게 액세스하는 것이 아니라 어느한 순간에 특정부분을 집중적으로 참조하는 것을 말한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"지역성 메모리의 위치와 접근 시간에 따라서 공간적, 시각적인 특성을 보인다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"공간적 지역성(Spatial Locality)"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"최근에 참조된 주소의 인접한 데이터가 참조될 가능성이 높은 특성"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"a[0], a[1] 처럼 같은 데이터 배열에 연속적으로 접근할 때 참조된 데이터 근처에 있는 데이터가 잠시 후 사용될 가능성이 높다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"시간 지역성(Temporal Locality)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"최근에 참조된 주소의 내용이 재 참조될 가능성이 높은 특성"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"For, While 같은 반복문에 사용되는 조건 변수처럼 한번 참조된 데이터는 잠시 후 참조될 가능성이 높다.","type":"text"}]}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"캐시에 데이터를 저장할 때는, 참조 지역성(공간)을 최대한 활용하기 위해 해당 데이터뿐만 아니라 옆 주소의 데이터도 같이 가져와 미래에 쓰일 것을 대비해야 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"캐시 미스(Cache miss)란?","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"CPU가 데이터를 요청했을 때 캐시 메모리가 해당 데이터를 가지고 있다면 이를 캐시히트라고 부르며, 해당 데이터가 없어서 DRAM에 접근해야 한다면 캐시 미스 라고 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"캐시미스 유형엔 3가지가 있다."}]},{"items":[{"content":[{"inlineContent":[{"text":"Cold start miss(Compulsory miss)","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터에 처음 접근 시 캐시에 데이터를 올리기 위해 발생하는 캐시미스"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Capactiy miss","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"캐시의 용량이 부족하여 발생하는 미스"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉 프로그램 수행시 접근하는 데이터의 양이 캐시의 사이즈를 넘어갈 경우 발생한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"예를 들어 32K direct mapped cache를 달고 있는 컴퓨터에서 128k array data를 접근하는 경우 캐시는 array data를 모두 저장할 수 없으므로 용량 부족에 의한 캐시미스가 발생한다. 이러한 미스를 capacity miss라 한다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Conflict miss"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"캐시 메모리에 A와 B 데이터를 저장해야하는데, A와 B가 같은 캐시 메모리 주소에 할당되어서 나는 캐시미스."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"캐시미스 비율은 대체로 평균 10% 안쪽이기 때문에 캐시 메모리를 통해 컴퓨터 시스템의 평균 성능을 크게 향상 시킬 수 있으며, 클럭 속도, 코어 개수와 함께 컴퓨터 성능에서 매우 큰 비중을 차지한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그러나 많은 사람들이 캐시 메모리에 대해 잘 모르며 실제 캐시 메모리가 없이 클럭 속도가 더 높은 CPU가 클럭속도는 낮지만 캐시메모리가 있는 CPU보다 대체로 더 나쁜 성능을 보여준다."}]}]}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"요약","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"캐시는 재사용될 수 있을만한 자원을 특정 영역에 저장해놓는 것을 의미한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"캐싱된 데이터가 있다면 추가적인 자원을 소모하지 않고 caching Data를 가져다 쓸 수 있기 때문에 자원을 절약할 수 있고 애플리케이션의 처리 속도가 향상된다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Caching은 웹 브라우저부터 웹 서버, 하드 디스크 및 CPU에 이르기까지 다양한 방면에서 적용되고 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"iOS에서 캐싱은 memory caching과 disk caching 2가지 방법이 있다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Memory Caching","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"어플리케이션의 메모리 영역의 일부분을 캐싱에 사용하는 것을 의미한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"하지만 어플리케이션이 종료되어 메모리에서 해제되면 이 영역에 있던 리소스들은 OS에 반환되면서 메모리 캐싱되어있던 리소스들은 사라지게 된다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Disk Caching","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"데이터를 파일 형태로 디스크에 저장하는 것을 말한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"어플리케이션을 껐다 켜도 데이터가 사라지지 않지만, 디스크 캐싱이 반복적으로 발생하면 어플리케이션이 차지하는 용량이 커진다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"ex) 카카오톡에서 이미지나 동영상을 디바이스에 저장하지 않더라도 눈으로 보기 위해서는 파일을 다운로드 받아야하는데, 이때 다운로드 받은 파일들이 Disk Chching되어 앱을 종료했다가 실행해도 볼 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"특이 iOS에서는 NSCache를 제공하는데, 주로 메모리 캐싱에 사용되는 클래스이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"NSCache","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"주로 메모리 캐싱에 사용되는 클래스"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"메모리에서 해제될 때 자동으로 캐시된 내용이 제거되며, 메모리 자원이 부족해도 삭제 대상이 된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"NSCache는 Thread-Safe하기 때문에 여러 스레드에서 접근할 때에도 Cache에 lock을 걸어줄 필요가 없다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"key-value 형태의 데이터를 임시로 저장하는 데 사용할 수 있는 가변 컬렉션을 말한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"특징"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Linked List를 사용하여 데이터 추가 및 삭제에 효율적으로 동작한다. (탐색 시간 복잡도 O(n))","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"별도의 Dictionary를 두어 데이터 접근에도 용이하다 (데이터 접근 시간 복잡도 O(1))"}],"type":"paragraph"}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"NSDictionary와의 차이점","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"자동 메모리 관리(기본 제공)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"먼저 NSCache는 시스템 메모리가 꽉 차면 자동으로 캐시의 메모리를 정리해준다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"앱에서 메모리가 부족한데 메모리를 더 사용하려고 하면 데이터를 지우고 메모리를 해제하는 것","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"만약 NSDictionary를 Cache 용도로 사용하려고 한다면 메모리가 부족하다는 시스템 경고를 받았을 때 사전이 이용한 메모리를 정리하는 코드를 미리 직접 작성해야한다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Thread-Safe"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSCache는 Thread-Safe하기 때문에 캐시데이터를 읽고 쓰고 지울때마다 따로 lock을 해줄 필요가 없다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"동시에 여러 Thread가 접근하더라도 각자 lock을 잡아줄 필요가 없다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"하지만 NSDictionary는 Thread-safe하지 않으므로 데이터가 접근할 때 따로 처리해줘야 한다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Retain Key","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSCache는 key를 복사하지 않고 리테인한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"key가 복사를 지원하지 않는 객체일 수 있기 때문에 이러한 객체도 포용하기 위해서이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 복사를 지원하지 않는 객체와도 잘 동작한다."}]}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSCache Declaration"}]}]}],"type":"unorderedList"},{"code":["class NSCache : NSObject where KeyType : AnyObject, ObjectType : AnyObject"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"NSCache Property","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Cache 객체에는 다양한 자동 제거 정책이 있으며 이들을 통합하여 일부 항목을 제거한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이러한 자동 제거 정책에는 캐시 객체의 최대 개수를 제한하고 모든 캐시 객체의 최대 cost를 제한하는 두 가지 프로퍼티가 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"var countLimit: Int","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"캐시가 가질 수 있는 최대한의 객체 수","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"위 프로퍼티를 통해 캐싱하는 데이터의 개수를 제한할 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"만약 countLimit가 10으로 설정되어있는데, 11개의 데이터를 NSCache에 넣게 되면 1개는 자동으로 버린다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기본값은 0으로 개수 제한이 없다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"var totalCostLimit: Int","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"객체를 제거하기 전에 캐시가 보유할 수 있는 최대 비용"}]}]},{"content":[{"inlineContent":[{"text":"객체를 추가할 때 cost(Int)를 함께 설정할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"이 때, totalCostLimit은 cost들의 총합의 최댓값이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉 NSCache에 추가된 데이터들의 cost가 totalCostLimit에 도달하거나 넘게되면 NSCache는 데이터를 버린다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기본값은 0으로 비용 제한이 없다."}]}]}]}]}],"type":"unorderedList"},{"syntax":"swift","code":["<예시>","let cache: NSCache = NSCache()","cache.countLimit \/\/ 허용하는 key의 개수","cache.totalCostLimit \/\/ cost 합계의 최댓값"],"type":"codeListing"},{"items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"var evictsObjectsWithDiscardedContent: Bool"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"NSCache는 시스템에서 메모리를 너무 많이 사용하지 않도록 디자인되어 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그래서 캐싱된 데이터를 자동으로 지우는 다양한 정책을 사용하고 있고, 캐싱된 데이터가 너무 많은 메모리를 사용하면 시스템은 캐싱된 데이터를 삭제한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"code":"protocol NSDiscardableContent","type":"codeVoice"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"클래스 내 객체의 하위 구성 요소가 사용되지 않을 때 삭제되어도 된다면 이 프로토콜을 채택함으로써 응용 프로그램의 메모리 사용 공간을 줄일 수 있다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"func object(forKey: KeyType) -> ObjectType","type":"codeVoice"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"주어진 key와 연결된 값을 반환한다."}]}]}],"type":"unorderedList"}]}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"NSDiscardableContent","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"NSCache는 자동으로 객체의 캐싱 및 제거를 처리하지만 객체를 제거할 시기를 제어할 수는 없다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"더이상 필요하지 않은 객체를 제거하는 경우 NSPurgeableData, NSDiscardableContent 프로토콜을 채택한 타입을 사용할 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"NSDiscardableContent는 필요한 순간에 사용하던 메모리를 반납해야하는 객체를 위한 인터페이스를 정의하는 프로토콜이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSDiscardableContent 프로토콜은 내부족으로 객체의 life cycle을 다루는 Counter 변수가 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Counter 변수는 현재 객체가 다른 객체에서 사용중인지 여부를 추적하는 용도 사용된다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"기본적으로 Counter 변수는 1로 초기화 된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"beginContentAccess() 메소드를 호출하면 1씩 증가하게 되고 endContentIfPossible() 메소드를 호출하여 해당시점에 관련 메모리가 해제된다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"NSPurgeableData","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 클래스는 더이상 필요하지 않을 때 사용한 메모리를 반납해야하는 mutable data 객체이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 객체는 자동 반납되는 데이터(autopurging data)를 제공한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이 기능은 이 객체가 자동으로 반납되면 캐시에서도 자동으로 제거된다는 것을 말한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"class NSPurgeableData: NSMutableData","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이 클래스는 NSDiscardableContent 프로토콜을 준수하는 NSMutableData의 서브클래스다"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSPurgeableData는 NSDiscardableContent 프로토콜의 기본 구현이 되어있다."}]}]},{"content":[{"inlineContent":[{"text":"NSPurgeableData 객체가 NSCache에 추가되었다면 반납될 수 있는 데이터 객체가 반납되면 자동으로 캐시에서 제거된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"반납될 수 있는 데이터 객체에 접근해야 한다면, 사용하는 동안 반납되는 것을 막기 위해 beginContentAccess()를 호출해야한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 객체 사용이 끝났을 경우 객체의 메모리를 반납해도 된다고 알리는 endContentAccess()를 호출해야 한다."}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"NSCache 구현"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"연결리스트로 데이터를 캐싱하는 이유에 대해서 명확히 서술되어 있지는 않지만, 캐시는 중간에 있는 데이터를 추가, 삭제하는 것이 빈번하기 때문에 이를 효율적으로 하기 위해(배열로 해당 작업을 수행시 데이터를 앞으로 당기거나 뒤로 데이터를 모두 밀어아하는 작업이 추가적으로 발생한다.) 연결리스트로 구현된 것이라고 생각해볼 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"또한 NSCache는 별도로 Dictionary를 두어 데이터의 접근도 O(1)에 수행할 수 있도록 제공하고 있다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"code":["\/\/ NSCache","open class NSCache : NSObject {",""," private var _entries = Dictionary>()"," private var _head: NSCacheEntry?","}"],"type":"codeListing","syntax":"swift"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@ryalya\/iOS-CS-Study-Cache-Memory%EB%9E%80"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/hcn1519.github.io\/articles\/2018-08\/nscache","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/foundation\/nscache"}]}]}]}]}],"type":"unorderedList"}]}],"metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","title":"220110 NSCache"},"variants":[{"paths":["\/documentation\/arinote\/220110_nscache"],"traits":[{"interfaceLanguage":"swift"}]}],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220113_cache,_pagination.json b/docs/data/documentation/arinote/220113_cache,_pagination.json new file mode 100644 index 0000000..43d0c96 --- /dev/null +++ b/docs/data/documentation/arinote/220113_cache,_pagination.json @@ -0,0 +1 @@ +{"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220113_Cache,_pagination","interfaceLanguage":"swift"},"metadata":{"roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}],"title":"220113 Cache, pagination"},"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","level":1,"type":"heading","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"text":"1월 13일 (목)","type":"text"}]},{"level":2,"text":"학습 내용","type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"오픈 마켓 프로젝트 STEP 2 피드백 답변 코멘트"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"오픈 마켓 프로젝트 STEP Bonus 진행해보기"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Cache 활동학습","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2,"type":"heading","text":"고민한 점 \/ 해결 방법"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[pagination]","type":"text"}]}]},{"type":"codeListing","syntax":"swift","code":[" func scrollViewDidScroll(_ scrollView: UIScrollView) {"," let contentHeight = scrollView.contentSize.height \/\/ 스크롤뷰의 전체 크기"," let yOffset = scrollView.contentOffset.y \/\/ 현재 스크롤하고 있는 위치"," let heightRemainBottomHeight = contentHeight - yOffset"," let frameHeight = scrollView.frame.size.height"," if heightRemainBottomHeight < frameHeight ,"," let hasNextPage = page?.hasNext, hasNextPage {"," currentPage += 1"," self.requestProducts()"," }"," }"]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"현재 스크롤하고 있는 위치, 스크롤 뷰의 전체 크기를 구한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"스크롤뷰 전체 높이 - 현재 스크롤 하고 있는 위치","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"계산한다. 현재 스크롤하고 있는 yOffset 같은 경우 스크롤을 내릴 때마다 값이 늘어난다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 계산한 해당 값(heightRemainBottomHeight)을 스크롤 뷰의 실제 사이즈(frameHeight)와 비교하여 다음 페이지가 존재하는 경우 네트워킹을 시도하도록 설계하였다."}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"스크롤을 할 수록 "},{"code":"heightRemainBottomHeight","type":"codeVoice"},{"type":"text","text":" 값이 적어진다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"frameHeight"},{"type":"text","text":" 높이만큼 스크롤을 할 경우 "},{"code":"heightRemainBottomHeight","type":"codeVoice"},{"text":"가 작아지는 순간이 온다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그때 다음페이지가 존재하는지 확인후 "},{"type":"codeVoice","code":"네트워킹"},{"text":"을 시도하고","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"네트워킹이 끝나면 콜렉션뷰를 ","type":"text"},{"code":"reloadData","type":"codeVoice"},{"text":"를 호출하여 추가된 데이터를 가지고 셀을 더 추가한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"추가하고 나면 스크롤뷰의 전체 크기인 "},{"code":"contentHeight","type":"codeVoice"},{"text":"의 크기도 늘어난다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"선언한 각 변수를 print를 찍어보며 스크롤을 내려보면 이해가 더 쉽다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/mOZGNPY.gif"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 방법은 이미지 캐싱을 처리하지 않으면 버벅이는 단점이 있다. 이미지 캐싱을 처리하면 단점은 완전히 사라지는 것을 확인하였다."}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Cache 활동학습]"}]}]},{"level":3,"text":"캐시란 무엇이고 어떤 역할일까?","type":"heading","anchor":"%EC%BA%90%EC%8B%9C%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EA%B3%A0-%EC%96%B4%EB%96%A4-%EC%97%AD%ED%95%A0%EC%9D%BC%EA%B9%8C"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"자주 사용하는 데이터를 임시저장"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"성능 향상"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터를 재사용"}]}]},{"content":[{"inlineContent":[{"text":"자주 사용되는 데이터를 임시로 저장해두는 것","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"성능의 향상을 위해 데이터를 임시로 저장해주는 것"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"캐싱된 데이터가 있다면 추가적인 자원을 소모하지 않고 caching Data를 가져다 쓸 수 있기 때문에 자원을 절약할 수 있고 애플리케이션의 처리 속도가 향상된다."}]}]}],"type":"unorderedList"},{"level":3,"anchor":"%EB%AA%87-%EA%B0%80%EC%A7%80-%EA%B8%B0%EC%A4%80%EC%9C%BC%EB%A1%9C-%EC%BA%90%EC%8B%9C%EB%A5%BC-%EA%B5%AC%EB%B6%84%ED%95%B4%EB%B3%BC%EA%B9%8C","type":"heading","text":"몇 가지 기준으로 캐시를 구분해볼까?"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"데이터를 사용하는 빈도"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"CPU와의 거리"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터의 특성(큰 크기의 이미지?, 바뀌지 않는 정보)"}]}]}]},{"text":"캐시는 클라이언트 서버 구분이 있나?","anchor":"%EC%BA%90%EC%8B%9C%EB%8A%94-%ED%81%B4%EB%9D%BC%EC%9D%B4%EC%96%B8%ED%8A%B8-%EC%84%9C%EB%B2%84-%EA%B5%AC%EB%B6%84%EC%9D%B4-%EC%9E%88%EB%82%98","type":"heading","level":3},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"캐시는 구분되어질 수 있을 것 같다"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"클라이언트는 local에 저장할 수 있는 캐시가 있을 것"}]}]},{"content":[{"inlineContent":[{"text":"서버는 여러 요청에 대해 저장할 수 있는 캐시가 있을 수 있을 것","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"서버 부하를 줄이기 위해 서버에 자주하는 요청을 캐싱해둔다."}]}]}],"type":"unorderedList"}]}]}]}]},{"text":"캐시는 어디에 저장할까?","type":"heading","level":3,"anchor":"%EC%BA%90%EC%8B%9C%EB%8A%94-%EC%96%B4%EB%94%94%EC%97%90-%EC%A0%80%EC%9E%A5%ED%95%A0%EA%B9%8C"},{"items":[{"content":[{"inlineContent":[{"text":"CPU 레지스터","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Ram 메모리"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"어플리케이션의 메모리 영역의 일부분을 캐싱에 사용하는 것을 의미"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"디스크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터를 파일 형태로 디스크에 저장하는 것을 의미"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"앱 내부 디스크(UserDefaults, CoreData)","type":"text"}]}]}]}]}],"type":"unorderedList"},{"text":"또 다른 기준이 있나?","level":3,"type":"heading","anchor":"%EB%98%90-%EB%8B%A4%EB%A5%B8-%EA%B8%B0%EC%A4%80%EC%9D%B4-%EC%9E%88%EB%82%98"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"데이터가 저장되는 계층에 따른 구분"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"L1, L2, L3 캐시 (비용, 속도, 용량에 따른 구분)","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"읽기 및 쓰기에 따른 구분"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"읽기 캐시","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"한번 읽어들인 데이터를 캐시에 저장 후 추후 read시 사용","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"쓰기 캐시"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"읽기 캐시의 기능 뿐 아니라 write시 디스크에 바로 올리지 않고 캐시에 임시로 저장후 추후 캐시 데이터를 모아 한번에 디스크에 저장"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"매번 변경 시 디스크에 여러번 저장할 필요가 없다."}]}]}]}]}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"level":3,"type":"heading","text":"캐시를 구현할 때 고려해야 하는 캐시 운용 정책에는 어떤것들이 있을까?","anchor":"%EC%BA%90%EC%8B%9C%EB%A5%BC-%EA%B5%AC%ED%98%84%ED%95%A0-%EB%95%8C-%EA%B3%A0%EB%A0%A4%ED%95%B4%EC%95%BC-%ED%95%98%EB%8A%94-%EC%BA%90%EC%8B%9C-%EC%9A%B4%EC%9A%A9-%EC%A0%95%EC%B1%85%EC%97%90%EB%8A%94-%EC%96%B4%EB%96%A4%EA%B2%83%EB%93%A4%EC%9D%B4-%EC%9E%88%EC%9D%84%EA%B9%8C"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"보안성"}]},{"items":[{"content":[{"inlineContent":[{"text":"캐시 접근 및 저장 관련","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"데이터 수명"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"캐시 교체 정책 (Cache Replacement Policy)","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터 사용시간에 따른 교체 (사용시간 작은 것 부터)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"LRU, FIFO, LFU 구조에 따라 교체"}]}]},{"content":[{"inlineContent":[{"text":"Random, NUR, Optional 등등","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"저장하는 시점에 따른 정책"}],"type":"paragraph"}]}],"type":"unorderedList"},{"level":3,"type":"heading","anchor":"iOS-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-%EC%BA%90%EC%8B%B1%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95%EC%97%90%EB%8A%94-%EB%AC%B4%EC%97%87%EB%AC%B4%EC%97%87%EC%9D%B4-%EC%9E%88%EC%9D%84%EA%B9%8C","text":"iOS 환경에서 캐싱하는 방법에는 무엇무엇이 있을까?"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"메모리 캐싱 방법 : NSCache, URLCache 등등.."}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"어플리케이션의 메모리 영역의 일부분을 캐싱에 사용하는 것을 의미"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"어플리케이션이 종료되어 메모리에서 해제되면 이 영역에 있던 리소스들은 OS에 반환되면서 메모리 캐싱되어있던 리소스들은 사라지게 된다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"디스크 캐싱 방법 : FileManager 를 통해서 Cache 디렉토리를 관리할 수 있다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"어플리케이션을 껐다 켜도 데이터가 사라지지 않지만 디스크 캐싱이 반복적으로 발생하면 어플리케이션이 차지하는 용량이 커진다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"ex) 카카오톡에서 이미지나 동영상을 디바이스에 저장하지 않더라도 눈으로 보기 위해서는 파일을 다운로드 받아야하는데, 이때 다운로드 받은 파일들이 Disk Caching되어 앱을 종료했다가 실행해도 볼 수 있다.","type":"text"}],"type":"paragraph"}]}]}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"heading","text":"- AlamofireImage 라이브러리와 Kingfisher 라이브러리의 캐시 정책은 어떤 차이가 있나요?","anchor":"AlamofireImage-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%99%80-Kingfisher-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%9D%98-%EC%BA%90%EC%8B%9C-%EC%A0%95%EC%B1%85%EC%9D%80-%EC%96%B4%EB%96%A4-%EC%B0%A8%EC%9D%B4%EA%B0%80-%EC%9E%88%EB%82%98%EC%9A%94","level":3},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"AlamofireImage"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"AutoPurgingImageCache는 CachedImages라는 Array를 들고 있다. disk는 사용하지 않는다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"memory warning notification을 받으면 AutoPurgingImageCache의 removeAllimages를 호출한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"AutoPurgingImageCache에 이미지를 add했을 때 메모리 용량을 설정값보다 많이 차지하고 있는 경우 lastAccessData로 정렬하여 지운다. - LRU(Least Recently Used) 방식"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"URLCache는 지워주는 부분이 따로 없기 때문에 직접 지워줘야 한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"expire 정책이 없다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"reference","identifier":"https:\/\/github.com\/Alamofire\/AlamofireImage#image-cache","isActive":true}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"GIF 처리를 별도로 할수 없다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"직접 Custom Alamofire Response Serializer를 만들도록 안내하고 있다."}],"type":"paragraph"}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Kingfisher","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"메모리와 디스크 모두를 위한 하이브리드 캐시","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"열거형으로 디스크와 메모리가 정의되어있음.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"text":"만료날짜 및 크기 제한 설정 가능","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"이미지의 URL만 알고 있다면 킹피셔를 이용하여 코드 두줄로 이미지를 캐싱할 수 있다."}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["let url = URL(string: \"https:\/\/example.com\/image.png\")","imageView.kf.setImage(with: url)"]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"memory warning notification을 받았을 때는 clearMemoryCache를 호출한다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"memoryCache에 있는 것들을 전부 지운다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"앱이 꺼지거나 백그라운드 진입했을 때는 clearExpiredDiskCache를 호출한다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"expire 된 것들을 지운 다음 여전히 디스크 용량이 많이 차지하고 있으면 contentAccessData로 정렬해서 지운다","type":"text"}]}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"GIF 처리를 할 수 있다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"AnimatedImageView를 사용하면 메모리 로드를 줄일 수 있지만 CPU load가 크다.","type":"text"}]}]}]}]}]}]}],"type":"unorderedList"},{"anchor":"%EA%B0%81-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%97%90%EC%84%9C-%EC%BA%90%EC%8B%9C-%EA%B8%B0%EB%8A%A5%EC%9D%84-%EC%A7%80%EC%9B%90%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%B4-%ED%99%9C%EC%9A%A9%ED%95%98%EB%8A%94-Cocoa-Layer%EC%9D%98-%EC%A3%BC%EC%9A%94-%EA%B8%B0%EB%8A%A5%ED%81%B4%EB%9E%98%EC%8A%A4-%EA%B5%AC%EC%A1%B0%EC%B2%B4-%EB%93%B1%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B4-%EC%9E%88%EB%82%98%EC%9A%94","text":"- 각 라이브러리에서 캐시 기능을 지원하기 위해 활용하는 Cocoa Layer의 주요 기능(클래스, 구조체 등)은 무엇이 있나요?","type":"heading","level":3},{"items":[{"content":[{"inlineContent":[{"text":"기본 기능들을 가지고 어떻게 조합되어있는지?","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"AlamofireImage"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"URLCache (클래스)","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"URLSession"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"URLSessionDataTask","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"CachedURLResponse"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"Kingfisher","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"URLSession"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"NSCache","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"NSLock","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"FileManager"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"text":"- 만약 라이브러리에서 on disk cache를 지원한다면, 캐시를 저장하는 디스크 영역은 어디인가요?","type":"heading","level":3,"anchor":"%EB%A7%8C%EC%95%BD-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%97%90%EC%84%9C-on-disk-cache%EB%A5%BC-%EC%A7%80%EC%9B%90%ED%95%9C%EB%8B%A4%EB%A9%B4-%EC%BA%90%EC%8B%9C%EB%A5%BC-%EC%A0%80%EC%9E%A5%ED%95%98%EB%8A%94-%EB%94%94%EC%8A%A4%ED%81%AC-%EC%98%81%EC%97%AD%EC%9D%80-%EC%96%B4%EB%94%94%EC%9D%B8%EA%B0%80%EC%9A%94"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"code":"let cacheDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)[0]","type":"codeVoice"}],"type":"paragraph"}]}]},{"text":"추가미션 : 저번 네트워크 프로젝트에서 이미지 캐싱을 적용해보자.","type":"heading","level":3,"anchor":"%EC%B6%94%EA%B0%80%EB%AF%B8%EC%85%98--%EC%A0%80%EB%B2%88-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EC%97%90%EC%84%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%BA%90%EC%8B%B1%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%B4%EB%B3%B4%EC%9E%90"},{"code":["class MyCell: UITableViewCell {","... "," override func prepareForReuse() { \/\/ 이미지를 지우도록 설정"," picture.image = nil"," }","}","","class ViewController: UIViewController, UITableViewDataSource {","..."," let cache = NSCache() \/\/ 캐시 인스턴스 생성","...","","\/\/ UITableViewDataSource 프로토콜 채택후 테이블뷰 메소드 내부..."," func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {","","...",""," let task = URLSession.shared.dataTask(with: url) { data, response, error in"," "," "," if indexPath == tableView.indexPath(for: cell) {"," if let cachedData = self.cache.object(forKey: NSString(string: imageURL)) {"," DispatchQueue.main.async {\/\/ 캐싱된 데이터가 존재하는지 확인"," cell.picture.image = cachedData"," }"," return"," }"," }"," "," if let error = error {"," print(\"오류!!!!!!!!!! \", error.localizedDescription)"," return"," }"," guard let httpResponse = response as? HTTPURLResponse,"," (200...299).contains(httpResponse.statusCode) else {"," print(\"오류!!!!!!!!!!\")"," return"," }"," guard let data = data, let image = UIImage(data: data) else {"," print(\"데이터 변환 오류!!\")"," return"," }"," "," if indexPath == tableView.indexPath(for: cell) { \/\/ indexPath 확인하고있음."," self.cache.setObject(image, forKey: NSString(string: imageURL)) \/\/ 이미지 캐싱하고"," DispatchQueue.main.async {"," \/\/ 캐싱이 되어있지 않다면,"," cell.picture.image = image \/\/ 이미지 대입"," }"," }"," }"," "," task.resume()"," "," return cell"," }","}"],"type":"codeListing","syntax":"swift"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"https:\/\/green1229.tistory.com\/57","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/ios-development.tistory.com\/658","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/ios-development.tistory.com\/715"}]}]}]}]}]}]}],"sections":[],"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"schemaVersion":{"major":0,"minor":3,"patch":0},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220113_cache,_pagination"]}],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/mOZGNPY.gif":{"identifier":"https:\/\/i.imgur.com\/mOZGNPY.gif","type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/mOZGNPY.gif"}]},"https://github.com/Alamofire/AlamofireImage#image-cache":{"identifier":"https:\/\/github.com\/Alamofire\/AlamofireImage#image-cache","type":"link","title":"https:\/\/github.com\/Alamofire\/AlamofireImage#image-cache","titleInlineContent":[{"text":"https:\/\/github.com\/Alamofire\/AlamofireImage#image-cache","type":"text"}],"url":"https:\/\/github.com\/Alamofire\/AlamofireImage#image-cache"},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220114_implementing_modern_collection_views_custom.json b/docs/data/documentation/arinote/220114_implementing_modern_collection_views_custom.json new file mode 100644 index 0000000..a63a9ff --- /dev/null +++ b/docs/data/documentation/arinote/220114_implementing_modern_collection_views_custom.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"metadata":{"title":"220114 Implementing Modern Collection Views Custom","role":"article","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article"},"kind":"article","sections":[],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220114_Implementing_Modern_Collection_Views_Custom","interfaceLanguage":"swift"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220114_implementing_modern_collection_views_custom"]}],"primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","type":"heading","level":1},{"inlineContent":[{"text":"1월 14일 (금)","type":"text"}],"type":"paragraph"},{"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"iOS 14.0 부터 사용할 수 있는 컬렉션뷰 커스텀해보기"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/github.com\/leeari95\/ModernCollectionView_Example","isActive":true}]}]}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"text":"고민한 점 \/ 해결 방법","level":2,"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[UIWindow]","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"View 들을 담는 컨테이너"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자 인터페이스에 배경을 제공하며 이벤트 처리 행동을 제공하는 객체이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/HyGDGIY.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"시각적인 화면을 가지고 있지 않고 기능적인 면을 담당한다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"상호작용 처리, 라우팅 x축 레벨 설정, 좌표계 변환","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"자동으로 Xcode가 앱의 기본 Window를 제공한다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"최초 iOS 프로젝트에는 StoryBoard를 사용하여 앱의 View들을 정의","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Storyboard는 Xcode에서 자동으로 제공하는 Appdelegate 또는 SceneDelegate에 window 속성이 존재해야 가능하다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/r2NEzXR.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"최초 프로젝트 생성 시 스토리보드와 연결된 View를 표현하기 위해 Strong으로 UIWindow 객체가 참조되고 있는 것을 확인"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"주의할 점"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"UIWindow는 UIView의 서브클래스이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/3U8qTbZ.png","type":"image"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스토리보드가 아닌 코드로 View를 구성할 때에는 Window rootViewController 수정이 필요하다."}]}]}]},{"syntax":"swift","type":"codeListing","code":["func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {"," guard let windowScene = (scene as? UIWindowScene) else {"," return"," }"," window = UIWindow(windowScene: windowScene)"," let navigationController = UINavigationController(rootViewController: ViewController())"," window?.rootViewController = navigationController"," window?.makeKeyAndVisible()","}"]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"먼저 scene 메소드 내부에 rootView를 설정해준다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그리고 Main.storyboard 파일을 삭제한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"프로젝트를 클릭후 General 탭에서 Main Interface에 Main 텍스트를 삭제한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/7m5P5ML.jpg"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 info.plist로 이동해서 Storyboad Name을 삭제한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/JIEicgd.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"window.makKeyAndVisible() 의미"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"keyWindow로 설정"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"keyWindow란?"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"윈도우가 여러개 존재할 때 가장 앞쪽에 배치된 윈도우를 keyWindow라고 지칭한다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uiwindow\/1621610-makekey","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uicollectionviewcompositionallayout\/3600951-list"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"테이블뷰의 스타일("},{"type":"codeVoice","code":".plain"},{"type":"text","text":", "},{"code":".grouped","type":"codeVoice"},{"type":"text","text":", "},{"code":".insetGrouped","type":"codeVoice"},{"text":")과 같은 모양을 제공한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"또한 CollectionView의 List 전용 "},{"type":"codeVoice","code":".sidebar"},{"type":"text","text":", "},{"code":".sidebarPlain","type":"codeVoice"},{"type":"text","text":" 이라는 새로운 스타일을 사용하여 "},{"type":"codeVoice","code":"iPadOS 14"},{"type":"text","text":"에서 "},{"type":"codeVoice","code":"다중 열 앱"},{"type":"text","text":"을 구축할 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/m3ILpN3.png"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/ios-development.tistory.com\/314"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[UICollectionViewDiffableDataSource]","type":"text"}],"type":"strong"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DataSource하면 떠오르는 것은 기존에 사용하던 "},{"type":"codeVoice","code":"UICollectionViewDataSource"},{"text":"이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UICollectionViewDataSource를 conform하는 대신 ","type":"text"},{"type":"codeVoice","code":"UICollectionViewDiffableDataSource"},{"type":"text","text":"를 conform 하는 것"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기존 UICollectionViewDataSource는 "},{"code":"Protocol","type":"codeVoice"},{"type":"text","text":"이라 UIViewController가 이를 conform하곤 했다."}]}]},{"content":[{"inlineContent":[{"text":"하지만 새롭게 생겨난 UICollectionViewDiffableDataSource는 Protocol이 아니라 ","type":"text"},{"code":"Generic Class","type":"codeVoice"},{"type":"text","text":"이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"그리고 ","type":"text"},{"code":"UICollectionViewDiffableDataSourc","type":"codeVoice"},{"type":"text","text":"e가 "},{"type":"codeVoice","code":"UICollectionViewDataSource를 conform"},{"type":"text","text":"하고 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"애플이 Diffable DataSource 만든 이유는 ","type":"text"},{"identifier":"https:\/\/developer.apple.com\/videos\/play\/wwdc2019\/220\/","type":"reference","isActive":true},{"type":"text","text":"에서 잘 설명해주고 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/BaklZqb.png"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"새롭게 생긴 Diffable DataSource에는 performBatchUpdates()가 없다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"대신 Crashing, 번거로움, 복잡성, 처리하고 싶지 않은 모든것들이 없고 ","type":"text"},{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewdiffabledatasource\/3375811-apply","isActive":true,"type":"reference"},{"text":"라는 단일 메소드가 있다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[NSDiffableDataSourceSnapshot]"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Snapshot이라는 개념도 도입된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Snapshot은 간단히 말해서 현재 UI state의 truth이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Section과 item에 대해서 Unique identifiers가 있으며, indexPath가 아니라 이 "},{"type":"codeVoice","code":"Unique identifiers로 업데이트"},{"type":"text","text":"를 하게 된다."}]}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/www.youtube.com\/watch?v=5Q4KgyESHRA","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/stackoverflow.com\/questions\/19240054\/could-not-find-a-storyboard-named-main-in-bundle"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/www.biteinteractive.com\/cell-content-configuration-in-ios-14\/"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/zeddios.tistory.com\/1197"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/uicellaccessory"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/munokkim.medium.com\/wwdc20-%ED%95%9C%EA%B8%80%EB%B2%88%EC%97%AD-lists-in-uicollectionview-ed47aa6793f9"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/stackoverflow.com\/questions\/64736932\/uicollectionviewlistcell-always-sets-a-height-of-44-that-conflicts-with-anything","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@leeyoungwoozz\/iOS-Cell-configuration"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uicollectionviewdiffabledatasource","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/swiftsenpai.com\/development\/uicollectionview-list-custom-cell\/","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/github.com\/LeeKahSeng\/SwiftSenpai-UICollectionView-List","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uicollectionview","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/stackoverflow.com\/questions\/64049419\/uicollectionview-list-with-custom-configuration-how-to-pass-changes-in-cell-to","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/nsdiffabledatasourcesnapshot"}]}]}]}]}]}]}],"schemaVersion":{"minor":3,"major":0,"patch":0},"references":{"https://i.imgur.com/HyGDGIY.png":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/HyGDGIY.png"}],"identifier":"https:\/\/i.imgur.com\/HyGDGIY.png","alt":null},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/BaklZqb.png":{"alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/BaklZqb.png"}],"type":"image","identifier":"https:\/\/i.imgur.com\/BaklZqb.png"},"https://i.imgur.com/7m5P5ML.jpg":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/7m5P5ML.jpg"}],"identifier":"https:\/\/i.imgur.com\/7m5P5ML.jpg","alt":null},"https://github.com/leeari95/ModernCollectionView_Example":{"title":"커밋을 활용하여 단계[?]별로 쪼개보았다.","titleInlineContent":[{"text":"커밋을 활용하여 단계[?]별로 쪼개보았다.","type":"text"}],"url":"https:\/\/github.com\/leeari95\/ModernCollectionView_Example","type":"link","identifier":"https:\/\/github.com\/leeari95\/ModernCollectionView_Example"},"https://developer.apple.com/documentation/uikit/uicollectionviewcompositionallayout/3600951-list":{"type":"link","titleInlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[UICollectionViewCompositionalLayout.list]"}]}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uicollectionviewcompositionallayout\/3600951-list","title":"[UICollectionViewCompositionalLayout.list]","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uicollectionviewcompositionallayout\/3600951-list"},"https://i.imgur.com/3U8qTbZ.png":{"alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/3U8qTbZ.png"}],"type":"image","identifier":"https:\/\/i.imgur.com\/3U8qTbZ.png"},"https://i.imgur.com/JIEicgd.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/JIEicgd.png","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/i.imgur.com\/JIEicgd.png"},"https://developer.apple.com/videos/play/wwdc2019/220/":{"title":"Advances in UI Data Sources","titleInlineContent":[{"type":"text","text":"Advances in UI Data Sources"}],"url":"https:\/\/developer.apple.com\/videos\/play\/wwdc2019\/220\/","type":"link","identifier":"https:\/\/developer.apple.com\/videos\/play\/wwdc2019\/220\/"},"https://i.imgur.com/r2NEzXR.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/r2NEzXR.png","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/i.imgur.com\/r2NEzXR.png"},"https://developer.apple.com/documentation/uikit/uitableviewdiffabledatasource/3375811-apply":{"title":"apply","titleInlineContent":[{"type":"text","text":"apply"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewdiffabledatasource\/3375811-apply","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewdiffabledatasource\/3375811-apply"},"https://i.imgur.com/m3ILpN3.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/m3ILpN3.png","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/i.imgur.com\/m3ILpN3.png"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220117_-----_--_--,_uiimagepickercontroller.json b/docs/data/documentation/arinote/220117_-----_--_--,_uiimagepickercontroller.json new file mode 100644 index 0000000..1857ba3 --- /dev/null +++ b/docs/data/documentation/arinote/220117_-----_--_--,_uiimagepickercontroller.json @@ -0,0 +1 @@ +{"schemaVersion":{"minor":3,"patch":0,"major":0},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220117_-----_--_--,_UIImagePickerController"},"primaryContentSections":[{"content":[{"anchor":"TIL-Today-I-Learned","type":"heading","text":"TIL (Today I Learned)","level":1},{"type":"paragraph","inlineContent":[{"text":"1월 17일 (월)","type":"text"}]},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"type":"heading","text":"학습 내용"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"UIImagePickerController 사용","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"스위프트의 성능 이해하기"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"level":2,"text":"고민한 점 \/ 해결 방법","type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[UIImagePicketController]"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"인스턴스를 생성해서 present를 하기만 하면 간단히 앨범이나 카메라에 접근할 수 있는 기능","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"imgePicketController.sourceType을 이용하면 앨범이나, 카메라 접근을 설정할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"sourceType을 .camera로 설정했을 때 시뮬레이터에는 에러가 나므로 주의해야한다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"실제 기기에서만 테스트 가능."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"앨범, 카메라 접근권한 설정 방법","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로젝트 내에 info 파일을 클릭"}]}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이후 우클릭 하여 Add Row를 클릭","type":"text"}]}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Privacy를 입력하여 키를 추가해준다. Value에는 알림창에 나오는 메세지를 입력해준다.","type":"text"}]}]},{"content":[]},{"content":[{"inlineContent":[{"text":"UIImagePickerControllerDelegate의 메소드","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"imagePickerController(_ picker:, didFinishPickingMediaWithInfo:)","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이미지를 Picking을 했을 때 불리는 메소드다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"선택한 이미지를 앨범에 저장, 혹은 이미지 정보를 활용해 뷰를 구성할 때 주로 사용한다."}]}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[상품 등록 뷰 설계]"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"add버튼을 추가해둘 커스텀 Footer를 생성","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이미지가 5개가 된다면 (collectionView.numberOfItems(inSection: 0) == 5) 플로우 레이아웃 활용해서 푸터 숨김"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"imageView를 담고있는 커스텀 cell 생성"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"콜렉션뷰에 둘다 레지스터","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DataSource와 delegateFlowLayout 설정"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"기타 옵션","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"스크롤바 숨기기","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"collectionView.showsHorizontalScrollIndicator = false"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"버튼이나 이미지 cornerRadius 설정","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"addButton.layer.cornerRadius = 10"}],"type":"paragraph"}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/i41dM51.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/DyzyAOB.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Footer에 존재하는 버튼을 눌렀을 때 ViewController가 아닌 ","type":"text"},{"type":"codeVoice","code":"FooterView"},{"type":"text","text":"에서 이벤트를 받게 된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NotificationCenter을 활용하여 "},{"code":"버튼 이벤트를 ViewController에게 전달","type":"codeVoice"},{"type":"text","text":"해줄 수 있도록 구성."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"delegate"},{"type":"text","text":" 패턴이랑 둘중에 뭐가 적절한지 비교해봐야지"}],"type":"paragraph"}]}]}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[이벤트를 전달받은 뷰컨이 해야할일 정해보기]"}]}]},{"items":[{"content":[{"inlineContent":[{"code":"UIImagePickerController","type":"codeVoice"},{"type":"text","text":" 인스턴스 생성"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"delegate"},{"text":" 채택","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"편집유무 설정"}]}]},{"content":[{"inlineContent":[{"code":"UIImagePickerController","type":"codeVoice"},{"type":"text","text":" 인스턴스 "},{"type":"codeVoice","code":"present"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"imagePickerController(_ picker:, didFinishPickingMediaWithInfo:)","type":"codeVoice"},{"type":"text","text":" 메소드 구현"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"원본이미지, 수정한 이미지 선언"}]}]},{"content":[{"inlineContent":[{"text":"수정한 이미지가 없다면 원본이미지를 옵셔널 바인딩하도록 설정","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뷰컨에 있는 images 배열에 append하도록 로직 구성"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"콜렉션뷰를 reloadData"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"picket를 dismiss","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"콜렉션뷰 "},{"type":"codeVoice","code":"delegate"},{"text":" ","type":"text"},{"code":"didSelectItemAt","type":"codeVoice"},{"text":" 메소드를 활용","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"터치시 이미지를 삭제하도록 구현","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"images "},{"type":"codeVoice","code":"remove"},{"text":" 메소드 호출","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"collectionView "},{"code":"reloadData","type":"codeVoice"},{"text":" 호출","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"완성~"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/SIRf4tB.gif"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/3Lx25s6.gif","type":"image"}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[남은 고민거리]","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"버튼 이벤트 전달할 때 "},{"type":"codeVoice","code":"delegate"},{"text":" vs ","type":"text"},{"code":"NotificationCenter","type":"codeVoice"},{"type":"text","text":" 비교"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"사진이 추가될 때 마다 콜렉션뷰 자동 스크롤이 필요할듯","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사진을 터치할 때 삭제하겠냐고 물어보는 얼럿 추가 구현 필요할듯"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사진을 추가할 때 카메라, 앨범 선택하는 얼럿시트 띄우고 싶은데… 소스타입을 카메라로 바꾸면 에러가 나서 해결못함 (소스타입을 사용할 수 없담서…)"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"아 이건.. 시뮬레이터에서 카메라를 열라그래서 뜨는 에러란다. 실제 기기에서는 잘 작동하나보다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"카메라는 사실 요구사항에 없는 부분이라 제리랑 의논해봐야징"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[스위프트 성능 이해하기]","type":"text"}]}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Value Semantics"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Value Type Semantic \/ Copy by Value Semantic"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"identity가 아닌 Value(값)에만 의미를 둔다."}]},{"items":[{"content":[{"inlineContent":[{"text":"Int, Double 등의 기본 타입","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"포인터만 복사되는 참조(Reference) 시맨틱스와 비교된다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Objective-C, Java 등"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"스위프트엔 Objc에 없던 새로운 Value 타입을 도입","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"struct, enum, tuple"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"특징"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"변수 할당 시 Stack에 값 전체가 저장된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"다른 변수에 할당될 때 전체 값이 복사된다. (copy by value)","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"변수들이 분리된다. 하나를 변경해도 다른 것에 영향이 없다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Heap을 안쓰며 Reference Counting도 필요없다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"class vs struct","type":"text"}]},{"type":"unorderedList","items":[{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"text":"클래스는 하나의 identity 변수가 복사되어도 값이 하나를 향해 같은 값을 가진다.","type":"text"}]}]},{"content":[]},{"content":[{"inlineContent":[{"text":"값타입의 각자 변수는 복사되어도 분리되어있다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Value Semantics: 값에 의해 구분된다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Value semantic에서는 identity가 아니라 value가 중요하다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"각 변수는 값(value)에 의해 구분이 되어야 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 동치 관계여야 한다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"간단하다. Equatable을 구현하자. (단순히 데이터를 전달할 목적인 struct 변수를 말하는 것이 아님)"}]}]}]}]}]}]},{"content":[{"inlineContent":[{"text":"Value Type과 Thread","type":"text"}],"type":"paragraph"},{"items":[{"content":[]},{"content":[{"inlineContent":[{"text":"스레드 간 의도하지 않은 공유로부터 안전하다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"값 모두를 Copy하는데 성능은 괜찮을까?"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Copy는 빠르다"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"기본 타입들 enum tuple struct"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정해진 시간(constant time)안에 끝난다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"내부 데이터가 Heap과 혼용하는 struct의 경우"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"정해진 시간 + 래퍼런스 copy등의 시간","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"String, Array Set, Dictionary 등"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"쓰기 시 Copy-on-write로 속도 저하 보완"}]}]}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"스레드로 문제 생기는 것을 Immutable로 해도 되는것이 아닌가?","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"사실 immutable로 해도되지만 모두 다 잘되는 것은 아니다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"참조형이어도 값이 불변하면 Thread간에 문제생길 일이 없다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"함수형 패러다임과 같이 널리 전파되기도 하였다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Immutable은 cocoa에서도 꽤 써왔다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정말 Immutable이 언제나 답일까?"}]},{"items":[{"content":[{"inlineContent":[{"text":"답은 아니다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Objc에서 많이 쓰던 Immutable 방식","type":"text"}]}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"text":"비효율적이다.","type":"text"}]}]},{"content":[]},{"content":[]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Dashboard를 새로 갈아끼는 것인가?"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그래도 class도 중요한 경우"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"value보다 identity가 중요한 경우"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIView 같이 모든 변수에서 단 하나의 state를 갖는 개체"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"OOP 모델"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"여전히 상속은 아주 훌륭한 도구"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Objective-C 연동"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Indirect storage (특수한 경우 struct내의 간접 저장소 역할)","type":"text"}]}]}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"성능을 위해 고려할 것들","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"성능에 영향을 미치는 3가지"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Memory Allocation"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Stack or Heap","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Reference Counting"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"No or Yes"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Method Dispatch","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Static or Dynamic"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"Heap 할당의 문제","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"할당 시에 빈 곳을 찾고 관리하는 것이 복잡한 과정이다."}],"type":"paragraph"}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"text":"무엇보다 그 과정이 thread safe해야 한다는 점이 가장 큰 문제이다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"lock 등의 synchronization 동작은 큰 성능 저하 요소","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"반면 Stack 할당은?"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"단순히 스택포인터 변수값만 바꿔주는 정도","type":"text"}]}]}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Heap 할당 줄이기","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"heap 할당 줄이기 최적화 예제"}]}]},{"content":[]},{"content":[{"inlineContent":[{"type":"text","text":"매우 번번히 호출된다면 성능에 영향을 미칠 수 있다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"예를 들면 매우 큰 Loop 안에서 일어나는 경우"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Key를 Value type으로 바꿔보자.","type":"text"}]}]},{"content":[]},{"content":[]}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"Reference Counting의 문제","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"정말 자주 실행된다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"변수 Copy할 때 마다"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그러나 이것도 역시 가장 큰 문제는 Thread Safety 때문"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"카운트를 Atomic하게 늘리고 줄여야한다.","type":"text"}]}]}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Method Dispatch"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Static"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컴파일 시점에 메소드의 실제 코드 위치를 안다면 실행중 찾는 과정 없이 바로 해당 코드 주소로 점프할 수 있음"}]}]},{"content":[{"inlineContent":[{"text":"컴파일러의 최적화, 메소드 인라이닝(Inlining) 가능","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"메소드 인라이닝?","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"컴파일 시점에 메소드 호출 부분에 메소드 내용을 붙여넣는다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"효과가 있다고 판단하는 경우에만","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Call Stack 오버헤드 줄임","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"CPU icache나 레지스터를 효율적으로 쓸 가능성"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컴파일러의 추가 최적화 가능"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"최근 메소드들이 작으므로 더더욱 기회가 많다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"루프 안에서 불리는 경우 큰 효과","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Dynamic"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Reference 시맨틱스에서의 다형성(Polymorphism)","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"실제 타입을 컴파일 시점에 알 수가 없다. 때문에 코드 주소를 rutime에 찾아야 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Dynamic Method Dispatch의 문제"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Static에 비해 단지 이것이 문제. Thread Safety문제도 없다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"하지만 이로 인해 컴파일러가 최적화를 못하는 것이 큰 문제이다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"Static Dispatch로 강제하기","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"final, private 등을 쓰는 버릇"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 메소드, 프로퍼티등은 상속되지 않으므로 static하게 처리"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"dynamic을 쓰지 않는다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Objc 연동 최소화"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Objective-C Runtime을 통하게 된다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"WMO(whole module optimization)","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"빌드시에 모든 파일을 한번에 분석하여 static dispatch로 변환 가능한지 등을 판단하여 최적화해준다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"하지만 굉장히 느려진다…"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"디버그 빌드에 적용하는 것은 정신건강에 좋지 않다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"아직 안정화가 안되어있다.","type":"text"}]}]}]}]}]}]}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"정리"}],"type":"paragraph"}]},{"content":[]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스위프트의 추상화 기법들의 성능"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"추상화 기법들"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"class","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"클래스는 Heap과 Referencs Counting을 이용한다."}]}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"text":"성능 상관 없이 래퍼런스 시맨틱스가 필요하다면 써야함","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Identity, 상속 등등…","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"단 래퍼런스의 의도하지않은 공유로 인한 문제는 조심해야 한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"단 클래스에 final 키워드를 쓴다면?","type":"text"}]}]},{"content":[]},{"content":[{"inlineContent":[{"type":"text","text":"method Dispatch가 Static이 되서 이 부분에 대한 성능이 개선될 수 있다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"struct","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"구조체안에 어떤 타입이 들어가 있는지에 따라 성능이 크게 달라진다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"참조 타입이 없다면?"}],"type":"paragraph"}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참조 타입을 가진 구조체라면?"}]}]},{"content":[]},{"content":[]},{"content":[]},{"content":[{"inlineContent":[{"text":"Reference Counting이 한번 Copy할 때마다 2번씩 일어난다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"struct안에 참조 타입의 property 수 만큼 많아진다.","type":"text"}]}]},{"content":[]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"참조 타입을 많이 가지고있는 구조체는?"}],"type":"paragraph"}]},{"content":[]},{"content":[{"inlineContent":[{"text":"구조체 내 참조타입을 줄여보기","type":"text"}],"type":"paragraph"}]},{"content":[]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"protocol"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"코드 없이 API만 정의한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"상속 없는 다형성(Polymorphism) 구현이 가능하다."}]}]},{"content":[{"inlineContent":[{"text":"Objective C의 protocol, Java의 Interface 매우 유사함","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Value 타입인 struct에도 적용이 가능하다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"value semantics에서의 다형성","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"protocol을 이용한 값타입의 다형성"}],"type":"paragraph"}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"text":"의문점","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"변수 할당","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"클래스라면 주소값이니 모두 같은 사이즈지만 구조체인 Point와 Line은 사이즈가 다르다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"어떻게 Drawable에 메모리를 미리 할당해 놓고 값을 저장할까?","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Method Dispatch"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"클래스의 다형성 구조에선 V-Table을 통해서 찾았다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"상속이 아닌 protocol의 다형성 구조에선 V-Table이 없다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"어떻게 Point-draw와 Line.draw를 구분해서 호출할까?","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로토콜의 변수할당"}]}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이것을 해결하기 위해서 Existential Container을 활용한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"실제 값을 넣고 관리하는 구조"}],"type":"paragraph"}]},{"content":[]},{"content":[{"inlineContent":[{"text":"struct가 3 words 이하인 경우","type":"text"}],"type":"paragraph"}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"text":"struct가 3 words보다 큰 경우","type":"text"}]}]},{"content":[]},{"content":[{"inlineContent":[{"text":"어떻게 3 word를 구분해 할당하고 복사하지?","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Value Witness Table(VWT)를 사용한다.","type":"text"}],"type":"paragraph"}]},{"content":[]}]}]},{"content":[{"inlineContent":[{"text":"VWT","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Existential container의 생성\/해제를 담당하는 인터페이스이다."}]}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"protocol을 구현하는 타입마다 있다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"protocol Witness Table","type":"text"}],"type":"paragraph"}]},{"content":[]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Copy 동작 정리","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"value 타입이므로 값 전체가 복사된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"3words 이하인 경우","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"단순히 새로운 Existential container에 전체가 복사됨"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"3 words를 넘는 경우","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"새로운 Existential container 생성","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"힙에 할당되어 저장된 값 전체가 새로운 힙 할당후 복사된다."}]}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"큰 사이즈 protocol 타입의 copy","type":"text"}]}]},{"content":[]},{"content":[{"inlineContent":[{"type":"text","text":"개선해본다면?"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스토리지 클래스를 만든다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그렇게 된다면 Drawable을 할당할 때 스토리지의 Reference를 가져오게 되는 것이다.","type":"text"}]}]},{"content":[]},{"content":[{"inlineContent":[{"text":"따라서 힙 할당이 더 싼 Reference Counting으로 바뀌었다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"하지만 값을 바꾼다면?","type":"text"}],"type":"paragraph"}]},{"content":[]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"value타입인데 이러면 안된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 이런 경우에는 값을 할당할 때 새로운 스토리지를 복사하여 대입하도록 코드를 추가해주어야 한다."}]}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이거에 대한 큰 장점은 일반적으로 사용할 때는 전혀 메모리 할당이 추가적으로 일어나지 않지만, 쓸때에만 복사를 한다. 따라서 성능을 개선할 수가 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Existential Container"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"변수가 protocol 타입으로 정의된 경우 쓰인다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로토콜을 통한 더형성을 구현하기 위한 목적으로 쓰인다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"내부 동작이 복잡하긴 해도 성능이 class 쓰는 것과 비슷하다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"둘다 초기화 시 Heap 할당하여 사용"}]}]},{"content":[{"inlineContent":[{"text":"둘다 Dynamic dispatch (class 도 V-Type, protocol은 PWT)","type":"text"}],"type":"paragraph"}]}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"하지만 복사를 할때에는 다른데.."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"큰 사이즈 Protocol 타입의 copy","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Indirect Storage","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"복사 시 힙 할당 대신 Reference Counting으로 대체","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"class타입의 다형성 쓸때와 비슷한 수준"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Copy-on-Write","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Indirect storage를 값이 변경될 시점에 Heap 할당하여 복사"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"성능 저하를 최소화한다. (변경 동작에서만)","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"String, Array, Dictionary 등도 이런 개념으로 Value semantics를 구현한다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"text":"작은 사이즈의 protocol 타입","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/user-images.githubusercontent.com\/75905803\/149803520-5e5366cd-948f-481b-b0d3-455e0a0e5e10.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"큰 사이즈의 protocol 타입","type":"text"}]}]},{"content":[]}]}]}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"Generics","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"제네릭 타입도 프로토콜과 마찬가지로 VWT를 사용한다."}],"type":"paragraph"}]},{"content":[]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"text":"성능을 개선할 수 없을까?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Method 내에서는 Drawble의 실제 타입이 바뀌지 않는다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Static Polymorphism"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"실제 타입별로 메소드를 만들어 준다면?"}],"type":"paragraph"}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하지만 이걸 손으로 하면 Generics 쓰지 말란 말인가?"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"ㄴㄴ 컴파일러가 해준다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Generic 특수화(Specialization)"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"더 효과를 보려면 WMO(whole module Optimization)을 켜라"}],"type":"paragraph"}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"아직 너무 믿진 말자…"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"제네릭 타입 정리"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"정적 다형성(Static Polymorphism)","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"컴파일 시점에 부르는 곳마다 타입이 정해져있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"런타임에 바뀌지 않는다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"특수화(Specialization)가 가능하다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"특수화가 되지 않은 제네릭(작은 사이즈)"}]}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"특수화된 제네릭(작은 사이즈)"}]}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"특수화가 되지 않은 제네릭(큰 사이즈)"}]}]},{"content":[]},{"content":[{"type":"paragraph","inlineContent":[{"text":"특수화된 제네릭(큰 사이즈)","type":"text"}]}]},{"content":[]}]}]}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"스위프트 성능 이해 정리"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Objective-C에 비해 큰 향상이 있었으나 값타입과 프로토콜 타입 등의 성격을 고려해야 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"성능 최적화를 고려해야하는 경우의 예","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"렌더링 관련 로직 등 반복적으로 매우 빈번히 불리는 경우","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"서버 환경에서의 대용량 데이터 처리","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"추상화 기법의 선택","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"struct","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"엔티티 등 value 시맨틱이 맞는 부분"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"class"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"identity가 필요한 부분, 상속등의 OOP, Objective-C"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"generics"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"정적 다형성으로 가능한 경우","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"protocol","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"동적 다형성이 필요한 경우"}],"type":"paragraph"}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"고려할 수 있는 성능 최적화 기법"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"struct에 클래스 타입의 프로퍼티가 많으면","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"enum, struct 등 값타입으로 대체"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Reference Counting을 줄인다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Protocol 타입을 쓸 때 대상이 큰 struct이면"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Indirect storage로 struct 구조 변경"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"mutable해야하면 copy-on-write 구현"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Dynamic method dispatch를 static하게"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"final, private의 생활화"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"dynamic 사용 최소화"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Objc 연동 최소화 하기"}]}]}]}]}]}]}]}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/jinshine.github.io\/2018\/07\/06\/iOS\/UIImagePickerController%20%EC%98%88%EC%A0%9C\/"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/zeddios.tistory.com\/949"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/hururuek-chapchap.tistory.com\/64"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/videos\/play\/wwdc2016\/416\/"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/swift\/blog\/?id=27","type":"text"}]}]}]}]}]}],"kind":"content"}],"sections":[],"variants":[{"paths":["\/documentation\/arinote\/220117_-----_--_--,_uiimagepickercontroller"],"traits":[{"interfaceLanguage":"swift"}]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"kind":"article","metadata":{"role":"article","roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"title":"220117 스위프트의 성능 이해, UIImagePickerController"},"references":{"https://i.imgur.com/3Lx25s6.gif":{"identifier":"https:\/\/i.imgur.com\/3Lx25s6.gif","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/3Lx25s6.gif","traits":["1x","light"]}],"type":"image"},"https://i.imgur.com/DyzyAOB.png":{"identifier":"https:\/\/i.imgur.com\/DyzyAOB.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/DyzyAOB.png","traits":["1x","light"]}],"type":"image"},"https://user-images.githubusercontent.com/75905803/149803520-5e5366cd-948f-481b-b0d3-455e0a0e5e10.png":{"identifier":"https:\/\/user-images.githubusercontent.com\/75905803\/149803520-5e5366cd-948f-481b-b0d3-455e0a0e5e10.png","alt":"image","variants":[{"url":"https:\/\/user-images.githubusercontent.com\/75905803\/149803520-5e5366cd-948f-481b-b0d3-455e0a0e5e10.png","traits":["1x","light"]}],"type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/SIRf4tB.gif":{"identifier":"https:\/\/i.imgur.com\/SIRf4tB.gif","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/SIRf4tB.gif","traits":["1x","light"]}],"type":"image"},"https://i.imgur.com/i41dM51.png":{"identifier":"https:\/\/i.imgur.com\/i41dM51.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/i41dM51.png","traits":["1x","light"]}],"type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220118_uitextfield,_keyboard,_uitextview,_scrollview,_touchesbegan,_recognizer,_uiimage.json b/docs/data/documentation/arinote/220118_uitextfield,_keyboard,_uitextview,_scrollview,_touchesbegan,_recognizer,_uiimage.json new file mode 100644 index 0000000..a8abbf2 --- /dev/null +++ b/docs/data/documentation/arinote/220118_uitextfield,_keyboard,_uitextview,_scrollview,_touchesbegan,_recognizer,_uiimage.json @@ -0,0 +1 @@ +{"kind":"article","schemaVersion":{"patch":0,"major":0,"minor":3},"metadata":{"title":"220118 UITextField, Keyboard, UITextView, ScrollView, touchesBegan, Recognizer, UIImage","roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"role":"article"},"primaryContentSections":[{"kind":"content","content":[{"level":1,"type":"heading","text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"text":"1월 18일 (화)","type":"text"}]},{"text":"학습 내용","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UITextField"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"키보드 타입 설정하기","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"키보드가 화면가리는 문제를 해결하기"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"키보드를 화면에서 내리는 방법","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이미지의 사이즈를 줄이는 방법"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2,"text":"고민한 점 \/ 해결 방법","type":"heading"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[키보드 타입 설정하기]"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정말 간단하다."}]}]}]},{"type":"codeListing","syntax":"swift","code":["func setUpKeyboardType() { \/\/ 원하는 타입을 대입해주면 끝~"," nameTextField.keyboardType = .namePhonePad"," priceTextField.keyboardType = .decimalPad"," discountedPriceTextField.keyboardType = .decimalPad"," stockTextField.keyboardType = .numberPad","}"]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[키보드가 콘텐츠를 가리지 않도록 하는 방법]","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ScrollView를 활용하기"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"키보드의 위치만큼 스크롤의 위치를 바꿔주는 방식"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NotificationCenter 활용"}]},{"items":[{"content":[{"inlineContent":[{"text":"키보드가 나타나고 사라질 때마다 Notification의 알림을 받기","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"iOS 9 이상 버전을 타겟으로 앱을 만든다면 자동으로 removeObserver를 해준다. 따라서 신경쓰지 않아도 된다!","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/useyourloaf.com\/blog\/unregistering-nsnotificationcenter-observers-in-ios-9\/"}]}]}]}]}],"type":"unorderedList"}]}]},{"syntax":"swift","code":["private func setUpNotification() {"," NotificationCenter.default.addObserver("," self,"," selector: #selector(keyboardWillShow(_:)),"," name: UIResponder.keyboardWillShowNotification, \/\/ show"," object: nil"," )"," NotificationCenter.default.addObserver("," self,"," selector: #selector(keyboardWillHide(_:)),"," name: UIResponder.keyboardWillHideNotification, \/\/ hide"," object: nil"," )","}",""],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"알림을 받았을 때 호출할 메소드 구현하기"}]}]}]},{"type":"codeListing","syntax":"swift","code":["@objc private func keyboardWillShow(_ notification: Notification) {"," guard let userInfo = notification.userInfo as NSDictionary?,"," var keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue,"," let scrollView = self.superview?.superview as? UIScrollView,"," let view = self.superview?.superview?.superview else {"," return"," }"," var contentInset = scrollView.contentInset"," contentInset.bottom = keyboardFrame.size.height"," scrollView.contentInset = contentInset"," scrollView.scrollIndicatorInsets = scrollView.contentInset","}","","@objc private func keyboardWillHide(_ notification: NSNotification) {"," guard let scrollView = self.superview?.superview as? UIScrollView else {"," return"," }"," scrollView.contentInset = UIEdgeInsets.zero"," scrollView.scrollIndicatorInsets = scrollView.contentInset","}"]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Noti로 받은 keyboardFrame을 바인딩 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"콘텐츠의 상하좌우로 안쪽 여백을 주는 contentInset의 bottom을 keyboardFrame.size의 높이로 대입해준다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"ScrollView의 서브뷰 크기를 변경하지 않고 스크롤 뷰 크기를 확장한다는 의미","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/soulpark.files.wordpress.com\/2012\/12\/ec8aa4ed81aceba6b0ec83b7-2012-12-30-ec98a4ed9b84-5-12-32.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"키보드로 인해 가려지는 부분을 스크롤뷰 아래쪽으로","type":"text"},{"type":"codeVoice","code":" keyboardFrame 사이즈의 높이만큼 버퍼를 추가"},{"type":"text","text":"하여 스크롤뷰를 확장함으로써 키보드 가림현상을 해결하는 것이다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"contentInset을 변경하면 "},{"code":"scrollIndicatorInsets","type":"codeVoice"},{"type":"text","text":"에도 영향을 미친다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스크롤 시 보이는 스크롤 표시를 말한다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"scrollIndicatorInsets"},{"type":"text","text":"가 "},{"code":"contentInset으로","type":"codeVoice"},{"type":"text","text":" 추가한 버퍼공간에도 표시가 된다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이를 방지하기 위해서는 ","type":"text"},{"type":"codeVoice","code":"scrollIndicatorInsets"},{"text":" 속성도 같이 변경해주어야 한다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/xywzYXO.gif"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"이렇게 스크롤뷰를 활용하면 키보드가 화면가리는 현상을 손쉽게 해결할 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[그럼.. UITextView는…어떻게?]"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"텍스트 필드는 위 방법으로 해결이 되었지만 UITextView는 어떻게 스크롤이 따라오게 만들까?"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이 부분은 텍스트뷰의 스크롤 기능을 false로 제거한다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"TextView.isScrollEnabled = false","type":"codeVoice"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"그리고 오토레이아웃으로 TextView의 높이가 늘 고정되어있는 것이 아니라 늘어날 수도 있도록 "},{"code":"priority","type":"codeVoice"},{"text":"를 조절해주면 된다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"위와 같이 세팅한다면, 텍스트뷰가 안에 Text가 길어질 수록 높이가 늘어나고, 그에 따라 스크롤도 자동으로 내려온다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"해결해보면서 메모장도 이런 방식인건가? 했다…!","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/P3z2Ydx.gif"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[키보드를 화면에서 없애는 방법]"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"삽질을 좀 해본 결과 여~러 방법이 있었다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Recognizer를 등록해서 터치시 키보드를 사라지도록 하기."}],"type":"paragraph"}]}]},{"type":"codeListing","syntax":"swift","code":["\/\/ Recognizer를 활용해서 뷰컨을 터치하면 키보드 사라지는 코드","func hideKeyboard() {"," let tap = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))"," view.addGestureRecognizer(tap)","}","","@objc func dismissKeyboard() {"," view.endEditing(true)","}"]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"touchesBegan 메소드를 활용하여 View를 터치시 키보드를 사라지게 하기."}]}]}],"type":"unorderedList"},{"code":["override func touchesBegan(_ touches: Set, with event: UIEvent?) {"," super.touchesBegan(touches, with: event)"," self.endEditing(true)","} "],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"inlineContent":[{"text":"ScrollView에 keyboardDismissMode를 drag로 설정해주기","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"scrollView.keyboardDismissMode = .onDrag"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이번 프로젝트에서는 touchesBegan과 ScrollView에 keyboardDismissMode를 활용하여 키보드를 내릴 수 있도록 해주었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"addGestureRecognizer는 실험해보았는데, 이걸 등록했을 때 같은 뷰컨에 있는 collectionView의 delegate 메소드가 호출되지 않는 현상을 발견하게 되었다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"열심히 구글링 해보았지만 잘모르겠다…."}]}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[이미지를 리사이즈 하는 방법]","type":"text"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"크기를 줄이고, 화질을 낮춰서 압축을 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UIImage를 extension하여 기능을 구현했다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"최대 파일 크기를 지정하고 그 이하가 될 때까지 반복하며 이미지의 크기를 줄인다.","type":"text"}]}]}]},{"syntax":"swift","code":["extension UIImage {"," func resized(percentage: CGFloat) -> UIImage { \/\/ 사이즈를 퍼센트만큼 리사이즈하는 메소드"," let size = CGSize(width: size.width * percentage, height: size.height * percentage)"," return UIGraphicsImageRenderer(size: size, format: imageRendererFormat).image { _ in"," draw(in: CGRect(origin: .zero, size: size))"," }"," }",""," func compress() -> UIImage { \/\/ 호출시 Data의 count를 기준으로 용량에 맞게 화질을 낮춘다."," var compressImage = self"," var quality: CGFloat = 1 \/\/ 화질"," let maxDataSize = 307200 \/\/ 최대크기"," guard var compressedImageData = compressImage.jpegData(compressionQuality: 1) else {"," return UIImage()"," }"," while compressedImageData.count > maxDataSize {"," quality *= 0.8 \/\/ 20퍼센트 씩 감소"," compressImage = compressImage.resized(percentage: quality)"," compressedImageData = compressImage.jpegData(compressionQuality: quality) ?? Data()"," }"," return compressImage"," }","}"],"type":"codeListing"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[추가로 공부가 필요한 부분]","type":"text"}]}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"touchesBegan vs TapGestureRecognizer","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"https:\/\/velog.io\/@wonhee010\/touchesBegan-vs-TapGestureRecognizer","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Cell별로 내용에 맞춰서 dynamic height 정하기"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/i-colours-u.tistory.com\/8"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/www.vadimbulavin.com\/collection-view-cells-self-sizing\/"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"아래로 당겨서 새로고침 하기"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/hyongdoc.tistory.com\/367"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"페이지 컨트롤 활용방법"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"https:\/\/moonibot.tistory.com\/29","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uikeyboardtype","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/stackoverflow.com\/a\/32583809","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/stackoverflow.com\/questions\/68714952\/how-to-achieve-a-responsive-auto-scrolling-when-uitextview-is-placed-in-uiscroll\/68728597#68728597","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/stackoverflow.com\/questions\/37028511\/dismiss-keyboard-in-textview-while-scroll","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/soulpark.wordpress.com\/2012\/12\/30\/contentinset-and-scrollindicatorinsets-properties-of-uiscrollview\/"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/soulpark.wordpress.com\/2012\/11\/30\/uiview-frame-bounds-coordinate-conversion\/"}]}]}],"type":"unorderedList"}]}]}]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"sections":[],"variants":[{"paths":["\/documentation\/arinote\/220118_uitextfield,_keyboard,_uitextview,_scrollview,_touchesbegan,_recognizer,_uiimage"],"traits":[{"interfaceLanguage":"swift"}]}],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220118_UITextField,_Keyboard,_UITextView,_ScrollView,_touchesBegan,_Recognizer,_UIImage"},"references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/P3z2Ydx.gif":{"identifier":"https:\/\/i.imgur.com\/P3z2Ydx.gif","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/P3z2Ydx.gif"}],"type":"image"},"https://i.imgur.com/xywzYXO.gif":{"identifier":"https:\/\/i.imgur.com\/xywzYXO.gif","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/xywzYXO.gif"}],"type":"image"},"https://soulpark.files.wordpress.com/2012/12/ec8aa4ed81aceba6b0ec83b7-2012-12-30-ec98a4ed9b84-5-12-32.png":{"identifier":"https:\/\/soulpark.files.wordpress.com\/2012\/12\/ec8aa4ed81aceba6b0ec83b7-2012-12-30-ec98a4ed9b84-5-12-32.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/soulpark.files.wordpress.com\/2012\/12\/ec8aa4ed81aceba6b0ec83b7-2012-12-30-ec98a4ed9b84-5-12-32.png"}],"type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220120_uialertcontroller,_keychain.json b/docs/data/documentation/arinote/220120_uialertcontroller,_keychain.json new file mode 100644 index 0000000..2c30302 --- /dev/null +++ b/docs/data/documentation/arinote/220120_uialertcontroller,_keychain.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"content":[{"anchor":"TIL-Today-I-Learned","level":1,"text":"TIL (Today I Learned)","type":"heading"},{"type":"paragraph","inlineContent":[{"text":"1월 20일 (수)","type":"text"}]},{"level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading","text":"학습 내용"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"오픈마켓2 STEP 1 PR 구현 및 작성","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"Alert에 TextField 추가해보기","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"KeyChain 활동학습"}]}]}],"type":"unorderedList"},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"level":2,"text":"고민한 점 \/ 해결 방법","type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[Alert에 TexField를 추가하여 텍스트를 입력 받아보자]","type":"text"}]}]},{"type":"codeListing","code":["func alertInputPassword(complection: @escaping (String) -> Void) {"," var resultTextField = UITextField()"," let alert = UIAlertController(title: \"비밀번호를 입력해주세요\", message: nil, preferredStyle: .alert)"," alert.addTextField { userTextField in"," userTextField.isSecureTextEntry = true \/\/ 텍스트를 암호화해준다."," resultTextField = userTextField"," }"," let okAcrion = UIAlertAction(title: \"OK\", style: .default) { _ in"," guard let text = resultTextField.text else {"," DispatchQueue.main.async {"," self.showAlert(message: Message.unknownError) \/\/ 에러처리"," }"," return"," }"," complection(secret)"," }"," let cancelAction = UIAlertAction(title: \"Cancel\", style: .cancel)"," alert.addAction(okAcrion)"," alert.addAction(cancelAction)"," self.present(alert, animated: true, completion: nil)","}"],"syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"비밀번호를 입력받고 검증하는 alert을 만들어보았다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"먼저 대입할 TextField 인스턴스를 생성한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이후 alert을 선언할 때 textfield를 추가하고 resultTextField에 대입해준다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"텍스트필드를 추가로 구현하고 싶다면 alert.addTextField를 호출하면 된다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"text":"이후 okAction 핸들러 내부에서 resultTextField를 바인딩하여 해당 text를 사용할 수 있도록 completion 클로저에 담아준다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"syntax":"swift","code":["self.alertInputPassword { secret in"," \/\/ 사용시에 secret을 활용하여 코드를 짜주면 된다.","}"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"주의해야하는 점은 ActionSheet에서는 TextField 구현이 불가능하다."}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[KeyChain]"}]}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"iOS 앱 개발 프로젝트를 할 때 민감한 정보를 어디에 저장해야할까?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UserDefaults"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"데이터를 쉽게 저장할 수 있지만, 단순히 info 파일에 키-값 쌍을 텍스트 형태로 저장하기 때문에 OS를 탈옥하면 내용물을 볼 수 있다는 문제가 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"따라서 보안이 필요한 데이터에는 적합하지 않다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이를 방지하기 위해 암호나 API Token과 같은 민감한 정보를 KeyChain에 저장하는 것이 좋다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"KeyChain은 디바이스 안에 암호화된 데이터 저장 공간을 의미한다. 사용자는 암호화된 공간에 데이터를 안전하게 보관할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"무엇을 저장할까?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"로그인 및 암호 (해시)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"결제 데이터"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"알고리즘 암호화를 위한 키","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"등등.. 단순한 구조의 비밀을 유지하고 싶은 것들을 저장","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"text":"특징","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"사용자가 직접 제거하지 않는 이상 앱을 제거하고 설치해도 데이터는 남아있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"디바이스를 Lock하면 KeyChain도 잠기고 디바이스를 UnLock하면 KeyChain 역시 풀린다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"KeyChain이 잠긴 상태에서는 item들에 접근할 수도 복호화할 수도 없다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"KeyChain이 풀린 상태에서도 해당 item을 생성하고 저장한 어플리케이션에서만 접근이 가능하다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"같은 개발자가 개발한 여러 앱에서 키체인 정보를 공유할 수 있다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"KeyChain Service API","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/Wt1zzEL.png","type":"image"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"민감한 데이터를 암호화, 복호화 하며 재사용하는 행위를 보다 쉽고 안전하게 사용할 수 있게 한다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"KeyChain Items","type":"text"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/Zt3ZRXg.png","type":"image"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"키체인에 저장된 Data 단위. 키체인은 하나 이상의 KeyChain item을 가질 수 있다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"KeyChain Item Class"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"저장할 Data의 종류"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"kSecClassInternetPassword","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"kSecClassCertificate","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"kSecClassGenericPassword","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"kSecClassIdentity"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"kSecClassKey"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"대표적으로 인터넷용 아이디\/패스워드를 저장할 때 사용하는 kSecClassInternetPassword","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"인증서를 저장할 때 사용하는 kSecClassCertificate"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"일반 비밀번호를 저장할 때 사용하는 kSecClassGenericPassword 등이 있다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Attributes"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"KeyChain item을 설명하는 특성. item class에 따라 설정할 수 있는 attributes가 달라진다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"애플 문서를 보면 아래와 같이 각 아이템 클래스 별로 다양한 어트리뷰트가 존재한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/rBv9yRb.png","type":"image"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[활동학습 정리]"}]}]},{"items":[{"content":[{"level":3,"type":"heading","anchor":"%ED%82%A4%EC%B2%B4%EC%9D%B8%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C","text":"키체인이란 무엇일까?"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"OAuth 기반 서버에서 인증이 필요한 API를 호출할 경우 Access Toekn 사용해서 호출하는데, 해당 Access Token을 발급받았을 경우 어디에 저장을 할까?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UserDefaults나 다른 방법을 이용하는 경우에는 암호화 과정이 거쳐지지 않아서 좋지 않다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"이럴 경우 KeyChain을 활용한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Passwords, Cryptographic keys, Certs & identities, Notes 등을 암호화해서 저장해둘 수 있는 열쇠 묶음","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Password 뿐만 아니라 알아야하는 정보인 신용카드나 note도 저장할 수 있고, 인식할 필요는 없지만 필요한 것 (보안통신에 있어 필요한 정보, 다른 유저나 디바이스와 신뢰를 쌓기 위해서 필요한 정보)도 저장이 가능하다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"앱을 삭제해도 키체인 정보는 남아있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"특징","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기본적으로 App은 자기 자신의 키 체인에만 접근할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"iOS에서 키체인의 위치는 샌드박스(외부에서 받은 파일을 바로 실행하지 않고 보호된 영역에서 실행시켜봄으로써 잘못된 파일과 프로그램이 내부 시스템 전체에 악영향을 주는 것을 미연에 방지하는 기술) 외부이므로, 앱을 삭제해도 키체인에 저장된 정보는 삭제되지 않는다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"앱의 프로비저닝 파일을 이용해서 앱 간의 사용 경로를 구분하기 때문에, 동일한 앱이라도 프로비저닝 파일을 변경해서 빌드하면 기존 정보를 더이상 조회할 수 없다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"키체인 그룹을 사용하여 서로 다른 앱에서도 저장된 데이터를 공유할 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"비밀번호 또는 개인 키처럼 보호가 필요한 항목은 암호화되어 키 체인으로 보호되며, 인증서처럼 보호가 필요하지 않은 항목은 암호화되지 않은 채로 저장된다."}]}]},{"content":[{"inlineContent":[{"text":"키체인은 잠글 수 있어서, 잠기면 해제하기 전까지 저장된 데이터에 접근할 수 없지만 iOS에서는 기기의 잠금이 해제되는 순간 키체인의 잠금도 함께 해제된다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"샌드박스"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"외부에서 받은 파일을 바로 실행하지 않고 보호된 영역에서 실행시켜 봄으로써 잘못된 파일과 프로그램이 내부 시스템 전체에 악영향을 주는지 확인하는 기술. 이를 확장해서 iOS에서는 애플리케이션간에 데이터 공유가 불가능하도록 격리된 각자의 샌드박스 공간을 제공하는데, iOS 시스템 자체에는 파일을 함부로 쓸 수 없지만 샌드박스 내에서는 파일 쓰기가 허용된다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"구조","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"단순하게도 키체인은 파일시스템에 저장된 데이터베이스다. SQLite 데이터베이스로 구현되어 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"macOS에서 사용자나 애플리케이션은 원하는 만큼 키체인을 만들 수 있지만 iOS에서는 모든 앱에서 사용할 수 있는 하나의 키체인만 사용한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"키체인 아이템(key chain item)"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"키체인에 저장되는 데이터로 키체인은 여러개의 키체인 아이템을 가질 수 있다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"아이템 클래스(Item class)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"저장할 데이터의 종류이다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"ID\/PW, 인증서, 인터넷 비밀번호 및 클래스로는 인터넷용 아이디 패스워드를 저장할 때 사용하는 kSecClassInternetPassword","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"인증서를 저장할 때 사용하는 kSecClassCertificate","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"일반 비밀번호를 저장할 때 사용하는 kSecClassGenericPassword 등이 있다."}],"type":"paragraph"}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"어트리뷰트(Attributed)","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"아이템 클래스에 대한 속성이다. 아이템 클래스에 따라 설정할 수 있는 어트리뷰트 종류가 달라진다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"키체인은 아이템을 정의할 때에는 저장할 데이터에 맞는 아이템 클래스를 선택해야 한다. 각 아이템 클래스는 저장값의 특성에 따라 서로 다른 어트리뷰트를 제공하기 때문이다."},{"type":"text","text":" "},{"type":"text","text":"* 예시로 kSecClassGenericPassword 라는 아이템 클래스를 통해서 인증 토큰을 저장하려고 하는데, 여기에는 핵심 어트리뷰트가 kSecAttrAccount \/ kSecAttrService가 있다."},{"text":" ","type":"text"},{"type":"text","text":"* kSecAttrAccount"},{"text":" ","type":"text"},{"type":"text","text":"* 앱을 식별할 수 있는 서비스 아이디"},{"type":"text","text":" "},{"text":"* kSecAttrService","type":"text"},{"type":"text","text":" "},{"type":"text","text":"* 저장할 비밀번호에 대한 사용자 계정"},{"type":"text","text":" "},{"text":"* 그래서 키체인은 이 두개의 어트리뷰트를 메인 키로 하여 저장된 비밀번호를 식별한다.","type":"text"}]}]}]}]}]}]},{"content":[{"type":"heading","level":3,"text":"Keyed Archiver, User Defaults, CoreData와 다른 점은 무엇일까?","anchor":"Keyed-Archiver-User-Defaults-CoreData%EC%99%80-%EB%8B%A4%EB%A5%B8-%EC%A0%90%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Keyed Archiver"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/foundation\/nskeyedarchiver"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"객체같은 사용자 정의 타입을 USerDefaults에 데이터의 형태로 저장"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSCoding 프로토콜을 채택하고 NSKeyedArchiver, NSKeyedUnarchoiver 인코딩\/디코딩하여 사용"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"앱을 삭제하면 함께 삭제된다"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"인스턴스의 정보를 시스템에 저장","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"딕셔너리와 같이 key-value 쌍으로 저장하기 위해 썼던 클래스"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Objective-C에서 사용하기 위해 설계되었지만 현재도 사용할 수 있음","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Swift에서는 사용하는 일이 많이 없다","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Serialization 인스턴스를 파일 형태로 저장하기 위한 클래스","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Key-value 형태로 아카이빙","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"암호화 지원하지 않음"}]}]}]}]},{"content":[{"inlineContent":[{"text":"User Defaults","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/foundation\/userdefaults\/"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱 이용자의 환경설정 등 키-값 형태로 저장해둘 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"주로 단일 데이터인 값을 저장"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"앱 삭제 전까지 유지","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"key-value 쌍으로 저장"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"앱 전역적으로 사용하는 가벼운 저장소"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"암호화 지원하지 않음","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"CoreData"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/coredata\/"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"복잡하고 큰 데이터를 저장"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱이 많은 데이터를 필요로 하고 여러 다른 객체 간의 관계를 관리하고 객체에 빠르고 쉽게 접근해야한다면 CoreData를 사용하는 것이 좋다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"역할","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Persistence - 데이터 지속 관리"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Undo and Redo of individual or Batched Changes 데이터 변경사항에 대한 실행취소\/ 재실행 가능"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"재실행 가능","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"코어데이터의 변경 사항을 추적하여 이에 대한 취소작업이 가능"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Background Data Tasks 백그라운드 상의 데이터 작업 지원"}]}]},{"content":[{"inlineContent":[{"text":"View Synchronization 뷰 동기화","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"테이블뷰\/컬렉션뷰 등의 UI에게 섹션, 행, 열 아이템을 관리해주는 DataSource를 제공하여 뷰와 데이터가 동기화 되도록 함"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"Versioning and Migration 버전 관리 및 데이터 이전 기능","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"오프라인 활용"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"undo, redo 이용"}]}]}]}]}]}]},{"content":[{"inlineContent":[{"text":"개략적인 비교","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"Though core data is slightly more complicated, it is useful when the stored information requires structure","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSKeyedArchiver is less complex and slower than core data, but is much simpler to use"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UserDefaults is the simplest method to persist data","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"iOS의 키체인과 macOS의 키체인의 차이는?"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"In iOS, apps have access to a single keychain (which logically encompasses the iCloud keychain). This keychain is automatically unlocked when the user unlocks the device then locked when the device is locked. An app can access only its own keychain items, or those shared with a group to which the app belongs. It can’t manage the keychain container itself.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"In macOS, however, the system supports an arbitrary number of keychains. You typically rely on the user to manage these with the Keychain Access app and work implicitly with the default keychain, much as you would in iOS. Nevertheless, the keychain services API does provide functions that you can use to manipulate keychains directly. For example, you can create and manage a keychain that is private to your app. On the other hand, robust access control mechanisms typically make this unnecessary for anything other than an app trying to replicate the keychain access utility.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"키체인의 개수가 다르다 (1개 vs 여러개)","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"macOS는 앱마다 다른 keychain을 사용할 수도 있고 iOS 같이 기본 keychain을 다 같이 사용할 수도 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"macOS는 keychain services API가 직접적으로 keychain을 관리할 수 있는 기능을 제공한다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"level":3,"type":"heading","anchor":"%ED%82%A4%EC%B2%B4%EC%9D%B8-%EC%84%9C%EB%B9%84%EC%8A%A4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%B4-k-%EC%A0%91%EB%91%90%EC%96%B4%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%EC%97%AC%EB%9F%AC-%ED%83%80%EC%9E%85%EA%B3%BC-%EA%B0%92%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%A9%EB%8B%88%EB%8B%A4","text":"키체인 서비스를 사용하기 위해 k- 접두어를 사용하는 여러 타입과 값을 사용합니다"},{"items":[{"content":[{"anchor":"k-%EC%A0%91%EB%91%90%EC%96%B4%EC%9D%98-%EB%B9%84%EB%B0%80%EC%9D%80","type":"heading","level":3,"text":"k- 접두어의 비밀은?"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"상수의 의미로 표현되는 것(변하지 않는 값)"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/library\/archive\/documentation\/CoreFoundation\/Conceptual\/CFDesignConcepts\/Articles\/NamingConventions.html#\/\/apple_ref\/doc\/uid\/20001110-CJBEJBHH","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/green1229.tistory.com\/56","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/metalkin.tistory.com\/20"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Core Foundation 등의 Core Framework 헤더 파일들을 보면, 헝가리언 네이밍 규칙을 따른 흔적들을 제법 볼 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"일단 ‘k’는 헝가리언 네이밍에 따라 사용된 걸로 보인다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Constants의 c는 이미 헝가리안에서 ‘char’의 ‘c’로 예약이 되어있는 상황이니 중복을 피하기 위해 동일한 발음 기호인 ‘k’를 사용한 것으로 추측된다."}]}]},{"content":[{"inlineContent":[{"text":"Core Foundation의 역사를 고려하면 헝가리언 네이밍을 따른 것이 어느정도 이해가 된다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"anchor":"%EC%96%B4%EB%96%A4-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC%EC%9D%98-%EA%B0%92%EC%9D%BC%EA%B9%8C","type":"heading","level":3,"text":"어떤 프레임워크의 값일까?"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"CoreFoundation"}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/4ekh36u.png"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"그 프레임워크와 Foundation 프레임워크와의 차이는?"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/vb3U81l.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"CoreFoundation: C 기반의 프레임워크. low-level functions이기 때문에 속도 측면에서 우수하나 메모리 누수 이슈(ARC를 지원하지 않음)가 존재한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/1ZEM0En.png","type":"image"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Foundation: objective-C 기반의 프레임워크. high-level functions. CoreFoundation과 동일한 Core Services Layer 계층에 속해있다."}]}]}]}]}],"type":"unorderedList"}]},{"content":[{"anchor":"%ED%82%A4%EC%B2%B4%EC%9D%B8%EC%9D%80-%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%A9%B4-%EC%A2%8B%EC%9D%84%EA%B9%8C","level":3,"type":"heading","text":"키체인은 언제 사용하면 좋을까?"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"로그인 비밀번호"}]}]},{"content":[{"inlineContent":[{"text":"결제 데이터","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"암호화를 위한 키","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"anchor":"%EC%9B%B9-%EC%84%B8%EC%85%98%EA%B3%BC-%EC%BF%A0%ED%82%A4%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C","text":"웹 세션과 쿠키는 무엇일까?","level":3,"type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"쿠키: 서버가 사용자의 웹 브라우저에 저장하는 데이터로, key: value의 형태로 저장된다. (예: 장바구니, 자동 로그인, 팝업 다시 보지않기 등)","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"세션: 쿠키를 기반으로 하며, 사용자 정보파일을 서버 측에서 관리한다. 비교적 느린 처리 속도를 가지며 동시에 서버의 자원을 사용하기 때문에 사용에 한계가 있다. (예: 로그인 상태유지 등)","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"text":"웹 세션과 쿠키와 키체인을 접목해서 활용해 볼 수 있을까?","anchor":"%EC%9B%B9-%EC%84%B8%EC%85%98%EA%B3%BC-%EC%BF%A0%ED%82%A4%EC%99%80-%ED%82%A4%EC%B2%B4%EC%9D%B8%EC%9D%84-%EC%A0%91%EB%AA%A9%ED%95%B4%EC%84%9C-%ED%99%9C%EC%9A%A9%ED%95%B4-%EB%B3%BC-%EC%88%98-%EC%9E%88%EC%9D%84%EA%B9%8C","level":3,"type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"활용해볼 수 있을 것 같다. 실제로도 활용하고 있는 듯 하다. 로그인을 한적이 있는 웹페이지를 접속하면 키체인으로 로그인하겠냐는 알림이 뜨기도 한다."}]}]}]}]}],"type":"unorderedList"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@leeyoungwoozz\/TIL-2021.05.21-Mon"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/green1229.tistory.com\/56","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/velog.io\/@rose6649\/IOS-Keychain-%ED%82%A4%EC%B2%B4%EC%9D%B8-%EC%82%AC%EC%9A%A9%EB%B2%95","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/adora-y.tistory.com\/entry\/iOS-KeyChain%EC%9D%B4%EB%9E%80-Swift%EC%BD%94%EB%93%9C%EB%A5%BC-%ED%86%B5%ED%95%B4-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@rose6649\/IOS-Keychain-%ED%82%A4%EC%B2%B4%EC%9D%B8-%EC%82%AC%EC%9A%A9%EB%B2%95"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/security\/certificate_key_and_trust_services\/keys\/storing_keys_in_the_keychain","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/security\/keychain_services","type":"text"}]}]}]}]}]}],"kind":"content"}],"sections":[],"schemaVersion":{"major":0,"patch":0,"minor":3},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220120_uialertcontroller,_keychain"]}],"kind":"article","identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220120_UIAlertController,_KeyChain"},"metadata":{"title":"220120 UIAlertController, KeyChain","modules":[{"name":"Ari's Notes"}],"role":"article","roleHeading":"Article"},"references":{"https://i.imgur.com/4ekh36u.png":{"identifier":"https:\/\/i.imgur.com\/4ekh36u.png","variants":[{"url":"https:\/\/i.imgur.com\/4ekh36u.png","traits":["1x","light"]}],"alt":null,"type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/Zt3ZRXg.png":{"identifier":"https:\/\/i.imgur.com\/Zt3ZRXg.png","variants":[{"url":"https:\/\/i.imgur.com\/Zt3ZRXg.png","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/rBv9yRb.png":{"identifier":"https:\/\/i.imgur.com\/rBv9yRb.png","variants":[{"url":"https:\/\/i.imgur.com\/rBv9yRb.png","traits":["1x","light"]}],"alt":null,"type":"image"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/1ZEM0En.png":{"identifier":"https:\/\/i.imgur.com\/1ZEM0En.png","variants":[{"url":"https:\/\/i.imgur.com\/1ZEM0En.png","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/vb3U81l.png":{"identifier":"https:\/\/i.imgur.com\/vb3U81l.png","variants":[{"url":"https:\/\/i.imgur.com\/vb3U81l.png","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/Wt1zzEL.png":{"identifier":"https:\/\/i.imgur.com\/Wt1zzEL.png","variants":[{"url":"https:\/\/i.imgur.com\/Wt1zzEL.png","traits":["1x","light"]}],"alt":null,"type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220121_selectedbackgroundview,_uirefreshcontrol,_---,_paging.json b/docs/data/documentation/arinote/220121_selectedbackgroundview,_uirefreshcontrol,_---,_paging.json new file mode 100644 index 0000000..724c73f --- /dev/null +++ b/docs/data/documentation/arinote/220121_selectedbackgroundview,_uirefreshcontrol,_---,_paging.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"content":[{"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","type":"heading","level":1},{"type":"paragraph","inlineContent":[{"text":"1월 21일 (금)","type":"text"}]},{"level":2,"type":"heading","text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"오픈마켓2 STEP 1 PR 피드백 반영","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UICollectionView의 ","type":"text"},{"code":"selectedBackgroundView","type":"codeVoice"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"아래로 당겨서 새로고침 기능"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"CollectionView로 Paging 구현하기"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"level":2,"type":"heading","text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"inlineContent":[{"inlineContent":[{"text":"[CollectionViewCell에 선택했다는…? 효과 추가해보기]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"UITableView같은 경우에는 기본적으로 seleted 했을 때, 회색 배경이 사라지지 않아서 delegate 메소드를 활용하여 deselect를 해주어야 배경색이 다시 원래대로 돌아왔었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하지만 UICollectionView 같은 경우에는 이 부분도 직접 설정을 해주어야 한다. 하는 방법은 정말 간단하다."}]}]}],"type":"unorderedList"},{"code":["selectedBackgroundView = UIView(frame: self.bounds)","selectedBackgroundView?.backgroundColor = .systemGray5"],"syntax":"swift","type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"셀을 초기화할 때 해당 코드를 추가해주면 셀을 선택했을 때 backgroundColoer가 입혀진 UIView로 바뀌는 로직인 것 같다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"하지만 이 코드만으로는 셀의 배경색이 그대로 바뀌어버린 채 원래 배경색으로 돌아오지 않는 현상이 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/jgslpbg.gif"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 아래 메소드도 추가해주어야 한다."}]}]}]},{"code":["\/\/ UICollectionViewDelegate...","func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {"," collectionView.deselectItem(at: indexPath, animated: true)","}"],"syntax":"swift","type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이렇게하면 셀을 선택했을 때, 선택되었다는 효과가 일어난다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/31itgHC.gif","type":"image"}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[피드백]"}],"type":"strong"}]},{"items":[{"content":[{"inlineContent":[{"code":"NSPhotoLibraryUsageDescriptiond","type":"codeVoice"},{"type":"text","text":": 사진 앨범 모든 기능에 대해 부여받는다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"code":"NSPhotoLibraryAddUsageDescription","type":"codeVoice"},{"type":"text","text":": 사진을 추가하기 위한 기능에 대해 부여받는다. (쓰기 전용)"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/VNDgoss.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/fF9NJdc.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이 부분은 정확하게 이해되지 않아서 차주에 다시 물어보기로 하였다."}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[아래로 당겨서 새로고침]","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIRefreshControl를 CollectionView에 추가하면 된다."}]}]}]},{"code":["\/\/ 인스턴스 생성","private var refreshControl: UIRefreshControl UIRefreshControl()","\/\/ addTarget 등록","collectionView.refreshControl = refreshControl"," refreshControl.addTarget(self, action: #selector(updateMainView), for: .valueChanged) ","","@objc private func updateMainView() {"," \/\/ 네트워킹 메소드를 포함하고 아래 블럭을 추가하여 뷰를 업데이트 하기"," DispatchQueue.main.asyncAfter(deadline: .now() + 1){"," self.refreshControl.endRefreshing()"," self.collectionView.reloadData()"," }","}"],"syntax":"swift","type":"codeListing"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[CollectionView로 Paging 구현하기]"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"먼저 CollectionView를 아래와 같이 선언해준다.","type":"text"}]}]}]},{"type":"codeListing","code":["class ViewController: UIViewController {"," "," let collectionView: UICollectionView = {"," let flowLayout = UICollectionViewFlowLayout() \/\/ 콜렉션뷰 내부 레이아웃을 잡기 위함"," flowLayout.scrollDirection = .horizontal"," let collectionView = UICollectionView(frame: .zero, collectionViewLayout: flowLayout)"," collectionView.translatesAutoresizingMaskIntoConstraints = false"," collectionView.register(CustomCell.self, forCellWithReuseIdentifier: \"cell\")"," collectionView.isPagingEnabled = true"," return collectionView"," }()","","..."],"syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"그리고 셀에 등록할 아이템들을 선언해준다."}],"type":"paragraph"}]}]},{"type":"codeListing","code":["\/\/ ViewController 내부","let emojies = [\"🔥\", \"🥰\", \"🥲\", \"👍\", \"👨‍🔬\", \"🤪\", \"🐸\", \"⚾️\"]"],"syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"아래와 같이 컬렉션뷰의 레이아웃도 잡아주자~"}],"type":"paragraph"}]}]},{"code":["private func setupCollectionViewLayout() {"," view.addSubview(collectionView)"," collectionView.backgroundColor = .systemGray"," collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true"," collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor,"," constant: -UIScreen.main.bounds.height \/ 1.4).isActive = true"," collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true"," collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true","}"],"syntax":"swift","type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"데이터 소스 설정"}],"type":"paragraph"}]}]},{"code":["extension ViewController: UICollectionViewDataSource { \/\/ 셀의 갯수와 재사용셀을 설정하기 위함"," func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {"," return emojies.count"," }"," "," func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {"," let cell = collectionView.dequeueReusableCell(withReuseIdentifier: \"cell\", for: indexPath) as! CustomCell"," cell.label.text = emojies[indexPath.item]"," "," return cell"," }","}"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"👉🏻 제일 중요한 부분이다. CollectionView의 경우 minimumLineSpacing이 기본적으로 값(10.0)이 들어가있다. 따라서 해당 부분을 아래와 같이 설정해주어야 스크롤 했을 때 밀림현상이 없어진다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"적용전에는 "},{"code":"itemSize","type":"codeVoice"},{"type":"text","text":"만 주었을 때이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/tY0ND4c.gif","type":"image"}]}]},{"content":[{"inlineContent":[{"text":"이후 ","type":"text"},{"type":"codeVoice","code":"minimumLineSpacing을 0으로 설정"},{"type":"text","text":"해주었더니 위에서 스크롤이 조금씩 밀리던 현상이 해결되었다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/wszbJ1I.gif"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UICollectionViewDelegateFlowLayout 코드","type":"text"}]}]}],"type":"unorderedList"},{"syntax":"swift","type":"codeListing","code":["extension ViewController: UICollectionViewDelegateFlowLayout { "," func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {"," let width = UIScreen.main.bounds.width"," return CGSize(width: width, height: width * 1.3)"," }",""," func collectionView("," _ collectionView: UICollectionView,"," layout collectionViewLayout: UICollectionViewLayout,"," minimumLineSpacingForSectionAt section: Int"," ) -> CGFloat {"," return 0"," }","}"]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/stackoverflow.com\/questions\/40443458\/uicollectionviewcell-backgroundcolor-not-change-when-selected","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/mobikul.com\/pull-to-refresh-in-swift\/"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/stackoverflow.com\/questions\/51336929\/uicollectionview-ispagingenabled","type":"text"}]}]}]}]}]}],"kind":"content"}],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220121_selectedbackgroundview,_uirefreshcontrol,_---,_paging"]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"metadata":{"roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}],"title":"220121 selectedBackgroundView, UIRefreshControl, 피드백, paging"},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220121_selectedBackgroundView,_UIRefreshControl,_---,_paging","interfaceLanguage":"swift"},"sections":[],"schemaVersion":{"major":0,"minor":3,"patch":0},"kind":"article","references":{"https://i.imgur.com/fF9NJdc.png":{"identifier":"https:\/\/i.imgur.com\/fF9NJdc.png","variants":[{"url":"https:\/\/i.imgur.com\/fF9NJdc.png","traits":["1x","light"]}],"type":"image","alt":null},"https://i.imgur.com/wszbJ1I.gif":{"identifier":"https:\/\/i.imgur.com\/wszbJ1I.gif","variants":[{"url":"https:\/\/i.imgur.com\/wszbJ1I.gif","traits":["1x","light"]}],"type":"image","alt":null},"https://i.imgur.com/tY0ND4c.gif":{"identifier":"https:\/\/i.imgur.com\/tY0ND4c.gif","variants":[{"url":"https:\/\/i.imgur.com\/tY0ND4c.gif","traits":["1x","light"]}],"type":"image","alt":null},"https://i.imgur.com/jgslpbg.gif":{"identifier":"https:\/\/i.imgur.com\/jgslpbg.gif","variants":[{"url":"https:\/\/i.imgur.com\/jgslpbg.gif","traits":["1x","light"]}],"type":"image","alt":null},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/31itgHC.gif":{"identifier":"https:\/\/i.imgur.com\/31itgHC.gif","variants":[{"url":"https:\/\/i.imgur.com\/31itgHC.gif","traits":["1x","light"]}],"type":"image","alt":null},"https://i.imgur.com/VNDgoss.png":{"identifier":"https:\/\/i.imgur.com\/VNDgoss.png","variants":[{"url":"https:\/\/i.imgur.com\/VNDgoss.png","traits":["1x","light"]}],"type":"image","alt":null}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220124_coredata,_dynamic_type,_uifontmetrics,_setcustomspacing,_viewforzooming,_------,_sql.json b/docs/data/documentation/arinote/220124_coredata,_dynamic_type,_uifontmetrics,_setcustomspacing,_viewforzooming,_------,_sql.json new file mode 100644 index 0000000..3da51a1 --- /dev/null +++ b/docs/data/documentation/arinote/220124_coredata,_dynamic_type,_uifontmetrics,_setcustomspacing,_viewforzooming,_------,_sql.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"metadata":{"title":"220124 CoreData, Dynamic Type, UIFontMetrics, setCustomSpacing, viewForZooming, 데이터베이스, SQL","role":"article","roleHeading":"Article","modules":[{"name":"Ari's Notes"}]},"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220124_CoreData,_Dynamic_Type,_UIFontMetrics,_setCustomSpacing,_viewForZooming,_------,_SQL","interfaceLanguage":"swift"},"schemaVersion":{"minor":3,"patch":0,"major":0},"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned","type":"heading","level":1},{"inlineContent":[{"text":"1월 24일 (월)","type":"text"}],"type":"paragraph"},{"text":"학습 내용","type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"오픈마켓2 STEP 2","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Dynamic Type을 지원하면서 텍스트에 bold 효과를 주는 법"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"CoreData 활동학습"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","type":"heading","level":2},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[Dynamic Type을 지원하면서 텍스트에 bold 효과를 주는 법]"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Apple의 UIFont가 제공하는 preferredFont를 사용하면 따로 굵기를 지정할 수 없고, 지정된 font만 사용해야 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"반대로 systemFont를 사용한다면 Dynamic Type이 동작하지 않는다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"extension을 사용하면 굵기를 지정해도 Dynamic Type을 지원하는 메소드를 구현할 수가 있다."}]}]}]},{"syntax":"swift","type":"codeListing","code":["extension UIFont {"," static func preferredFont(for style: TextStyle, weight: Weight) -> UIFont {"," let metrics = UIFontMetrics(forTextStyle: style)"," let desc = UIFontDescriptor.preferredFontDescriptor(withTextStyle: style)"," let font = UIFont.systemFont(ofSize: desc.pointSize, weight: weight)"," return metrics.scaledFont(for: font)"," }","}",""]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"preferredFont의 Style과 폰트 굵기를 파라미터로 받고, UIFont를 return 하는 함수를 구현","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Dynamic Type을 지원하기 위해 UIFontMetrics를 활용하여 Custom Font를 만들 것이다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"지정한 폰트가 다이나믹 타입을 지원하도록 해주는 유틸리티 객체이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"파라미터 ","type":"text"},{"type":"codeVoice","code":"style"},{"type":"text","text":"에 맞게 scale이 되는 구조."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"systemFont를 사용해 사이즈와 굵기를 전달하여, font 변수를 만든 후…"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"앞서 만든 metrics에 "},{"type":"codeVoice","code":"scaledFont"},{"type":"text","text":" 메소드를 호출하여 UIFont를 반환한다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"UIFontDescriptor로 style의 pontSize를 얻어내서 그걸 활용해서 custom font를 만들고, UIFontMetrics로 다이나믹 타입을 지원하는 Custom Font를 생성해내는 과정인 것 같다. (맞나?)"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[StackView 내부에 view 간의 spacing 설정]","type":"text"}],"type":"strong"}]},{"code":["func setCustomSpacing(_ spacing: CGFloat, "," after arrangedSubview: UIView)"],"syntax":"swift","type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"위 메소드를 사용하면 전체적인 spacing이 아니라 딱 주고싶은 구간에만 spacing을 설정해줄 수가 있었다. (아래 사진처럼…)","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/FTcY4Hd.png"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[이미지를 확대\/축소 하는 방법]"}]}],"type":"paragraph"},{"syntax":"swift","code":["extension ImageViewController: UIScrollViewDelegate {"," func viewForZooming(in scrollView: UIScrollView) -> UIView? {"," return self.imageView"," }","}"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ScrollView의 Delegate를 활용하면 쉽게 구현할 수 있었다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"위 코드처럼 zooming 할 뷰를 return 해주면, 해당 뷰를 zoom을 할 수 있었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"또한 ScrollView의 ZoomScale을 설정할 수도 있었다.","type":"text"}]}]}]},{"type":"codeListing","syntax":"swift","code":["private func setUpScrollView() {"," scrollView.delegate = self"," scrollView.zoomScale = 1.0"," scrollView.maximumZoomScale = 4.0"," scrollView.minimumZoomScale = 1.0"," scrollView.showsVerticalScrollIndicator = false"," scrollView.showsHorizontalScrollIndicator = false"," scrollView.decelerationRate = .fast","}"]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"위 처럼 ScrollView 프로퍼티 설정이 필요했었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이후 추가 구현을 위해 몇가지 공부해보았지만, 완벽하게 이해되지 않아서.. 좀더 공부가 필요할 것 같다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"탭 2번 했을 때 이미지 확대\/축소 되는 기능"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"ScrollView 내부에 imageView를 추가하여 페이징 구현","type":"text"}]}]}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[CoreData]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"데이터를 영구적으로 저장할 때 사용하는 기술"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"영구적이라는 것은 앱을 종료해도 데이터가 사라지지 않는다는 것"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하지만 MS SQL이나 My SQL과 같은 관계형 데이터 베이스는 아니다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"데이터베이스를 쉽게 사용할 수 있도록 도와주는 기술"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Core Data 문서에서만 Object Graph Management Tool이라고 설명하고 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"다양한 기능을 제공하는데, 데이터 베이스에 대한 이해가 없어도 데이터를 저장하는 기능을 쉽게 만들 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"모바일에 최적화된 프레임워크를 통해서 메모리를 적게 사용하면서 높은 성능을 제공한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"특히 Core Data 프레임워크는 iOS, macOS 플랫폼과 밀접하게 통합되어 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그래서 데이터를 저장하고 출력하는 기능을 적은 노력으로 쉽고 빠르게 개발할 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Core Data는 4가지 주요한 객체로 구성되어 있다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Persistent Store (NSPersistentStore)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이름 그대로 데이터를 영구적으로 저장한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"메모리에 저장되는 데이터와 달리 앱을 종료하거나 전원을 차단해도 계속 유지된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"기본적으로 4가지 영구 저장소를 제공하며 필요에 따라 직접 구현하는 것도 가능하다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"영구 저장소는 데이터를 메모리에 로딩하는 방식에 따라서 Atomic Store, Non-atomic Store로 구분한다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Atomic Store","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"데이터를 처리할 때 모든 데이터를 메모리에 로드해야 한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Non-atomic Store"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"필요한 부분만 메모리에 로드한다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Core Data는 Non-atomic Store인 SQList를 기본 저장소로 사용한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"적은 메모리 사용량과 빠른 성능을 제공하기 때문에 iOS 앱의 가장 적합한 저장소이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"나머지 세 저장소 모두 Atomic Store다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"XML Store"}]}]},{"content":[{"inlineContent":[{"text":"Binary Store","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"In-Memory Store","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"모든 데이터를 메모리에 로드해야하기 때문에 데이터 크기가 클수록 성능이 저하되는 단점이 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그래서 iOS 앱개발에서는 거의 사용되지 않는다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"다만 인메모리 저장소는 캐싱을 구현할 때 활용하기도 한다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Object Model(NSManagerObjectModel)"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"어떤 데이터가 저장되고 데이터들이 어떤 관계를 가지고 있는지 설명하는 객체이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"영구 저장소에 데이터를 저장하려면 어떤 구조로 저장해야하는지 파악해야하는데 여기에 필요한 모든 정보가 오브젝트 모델에 저장된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"오브젝트 모델은 NSManagerObjectModel 클래스로 구현되어 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"코드를 통해 직접 모델을 구성할 수 있지만 대부분 Xcode가 제공하는 모델 편집기를 사용해서 데이터모델을 구성한다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"Persistent Store Coordinator(NSPersistentStoreCoordinator)","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"영구저장소 코디네이터는 영구저장소에 저장되어 있는 데이터를 가져오거나 저장하는 객체"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Core Data가 다양한 영구 저장소를 사용할 수 있는 것은 영구 저장소 코디네이터가 데이터를 중계하기 때문이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컨텍스트 객체를 통해 데이터를 저장하면 오브젝트 모델을 통해 구조를 파악한 다음 영구 저장소에 알아서 저장한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"영구저장소 코디네이터는 NSPersistentStoreCoordinator 클래스로 구현되어있다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Managed Object Context(NSManagedObjectContext)"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Core Data를 통해 실행한 작업은 대부분 컨텍스트가 담당한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컨텍스트는 보통 스크래치 패드와 비교해서 설명한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Core Data에서 데이터를 생성하면 바로 영구 저장소에 저장되지는 않는다. 컨텍스트 내부의 임시데이터로 유지된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"컨텍스트에게 저장을 요청해야 영구저장소에 저장된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"반대로 컨텍스트를 저장하지 않고 종료하면 저장되지 않은 모든 데이터가 사라진다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"영구저장소에서 읽어온 데이터도 컨텍스트에서 처리된다. 이 데이터는 영구 저장소에 저장되어있는 원본 데이터에 대한 복사본이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그래서 컨텍스트에 있는 데이터를 수정해도 원본 데이터는 수정되지 않는다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"수정된 내용을 저장하려면 컨텍스트를 저장해야 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"컨텍스트는 NSManagedObjectContext 클래스로 구현되어있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"보통 하나의 컨텍스트를 사용하지만 필요에 따라서 여러 컨텍스트를 사용하기도 한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이 4가지 객체를 하나로 묶어서 Core Data Stack이라고 한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"대부분의 작업은 컨텍스트가 제공하는 API를 통해서 구현한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"나머지 세 객체는 초기화 시점을 제외하고는 자주 사용되지 않는다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"iOS 10에서 Persistent Container라는 새로운 개념이 도입되었다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컨테이너는 Core Data Stack을 캡슐화한 객체이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"NSPersistentContainer 클래스를 통해서 이전보다 쉽고 빠르게 Stack을 생성할 수 있다.","type":"text"}]}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"text":"[데이터베이스란?]","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"도입배경"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"독립된 파일 단위로 데이터를 저장하게 되면 데이터 종속성 및 중복성이 높아 무결성 위배 가능성이 높을 수 있음. 이러한 단점을 보안하고자 여러 시스템이 공용으로 데이터를 모아 관리하는데 데이터베이스를 구축하게 된다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"정의"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"시스템이 공용으로 사용하고 관리하는 데이터 집합"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"장점"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"종속성 및 중복성 최소화","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"일관성 및 무결성 유지","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"공유 및 보안성 강화"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"실시간 처리 가능","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"단점"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"전문가 부족","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"전산화 비용 증가","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"시스템이 복잡하고 느림","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"파일 회복이 어려움","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터베이스 관리 시스템(DBMS)"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"사용자와 데이터베이스 사이에서 사용자의 요구에 따라 데이터베이스 생성, 관리해주는 소프트웨어","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자 <-> DBMS <-> DB"}]}]},{"content":[{"inlineContent":[{"text":"사용자","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"DBA(DB관리자), 일반 사용자, 응용프로그램","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"text":"DBMS 종류","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"계층형, 네트워크형, 객체지향형, 관계형","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DBMS 프로그램"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Oracle, MySQL, MS SQL, Access 등등"}],"type":"paragraph"}]}]}]}]}]},{"content":[{"inlineContent":[{"text":"DBMS 기능 및 언어","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정의어(Data Definition Language)"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DBA 사용"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"데이터베이스를 생성하거나 자료 형태(type)와 구조 등을 수정하며 데이터를 이용하는 방식을 정의하는 기능","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"CREATE, ALTER, DROP"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"조작어(Data Manipulation Language)","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"사용자 사용","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터의 검색, 삽입, 삭제 ,변경 등을 처리하는 기능"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SELECT, INSERT, DELETE, UPDATe"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"제어어(Date Contorl Language)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"DBA 사용"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터의 무결성을 유지하기 위한 보안 및 권한 검사, 병행 제어 등의 기능을 정의하는 기능"}]}]},{"content":[{"inlineContent":[{"text":"COMMIT, ROLLBACK, GRANT, REVOKE","type":"text"}],"type":"paragraph"}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"관계형 데이터베이스 (Relational-DBMS = RDBMS)","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테이블(Table)을 이용한 구조"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/QIPX0do.png"}]}]},{"content":[{"inlineContent":[{"text":"테이블: 표, 개체 또는 릴레이션","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Xinny93.png"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"필드: 속성(Attribute), 열"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/SWov102.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"레코드: 튜플(Tuple), 행","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"도메인: 하나의 속성에서 취할 수 있는 원자값의 범위","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"필드의 개수 : 차수(Degree)","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"레코드의 개수: 기수(Cardinality)","type":"text"}],"type":"paragraph"}]}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"테이블의 특징"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"속성(필드)와 튜플(레코드)들은 유일하게 순서가 무관함"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"속성(필드)의 값은 분해할 수 없다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"속성(필드)의 값은 동일할 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"튜플(레코드)은 삽입, 삭제 등에 의해 계속 변한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"튜플(레코드)를 식별하기 위해 속성(필드)의 일부를 Key로 설정한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"속성(필드)는 Null 값을 가질 수 있으나, 기본키에 해당하는 속성(필드)는 Null값을 가질 수 없다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"데이터베이스 설계","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기본키 - 중복 불가능, null 값 불가능"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터베이스 설계 단계"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"개념적 설계 -> 논리적 설계 -> 물리적 설계","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정규화"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이상 현상이 일어나지 않도록 분해하는 과정"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"중복성, 종속성을 최소화하기 위한 작업","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"데이터베이스 구조(스키마)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"외부스키마"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"일반 사용자나 응용프로그래머 관점"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"개념스키마"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"논리적 구조 및 규칙"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"내부스키마","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"물리적 구조 설계자 관점 스키마"}]}]}],"type":"unorderedList"}]}]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[SQL이란?]","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Structured Query Language 약자"}]}]},{"content":[{"inlineContent":[{"text":"데이터베이스와 대화하기 위해 디자인된 언어","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"거의 모든 것들에겐 데이터베이스가 필요하다"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"데이터베이스는 데이터를 저장하는 곳","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"데이터베이스에는 2가지 종류가 있다"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Relational","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Non-Relational"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"혹은"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"SQL","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"mysql, postgreaql, sqlite","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Non-SQL"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"mongoDB, dynamoDB, couchDB","type":"text"}]}]}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"대다수의 회사, 정부기관, 은행 등등 다수 기관들이 SQL을 사용한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"SQL 데이터베이스는 어떻게 생겼고 어떻게 작동하는걸까?","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사실 SQL은 엑셀문서와 똑같이 생겼다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"데이터 베이스에 테이블이 있고, 엑셀문서에는 시트가 있겠다","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"테이블에는 row와 column이 있고 문서에도 row와 column이 있다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SQL에 4개의 column이 있다고 상상해보자"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"ID, 이름, 이메일, 나이"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"row에는 5명의 유저가 있다고 상상하자","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"만약 학생 테이블 유저의 모든 이메일을 갖고오고 싶다면 이를 위한 커맨드는"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SELECT email FROM students;"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"보다시피 그냥 보통 영어랑 똑같다. 프로그래밍 언어가 아니라 쿼리용 언어라서 그러하다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"이번엔 21살보다 많은 유저의 이메일을 갖고오고 싶다면?","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"SELECT email FROM students WHERE age > 21;","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이번엔 유저의 이메일이 naver인 경우의 나이를 찾아보자","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SELECT age FROM students WHERE email LIKE “%naver.com”;"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"이번엔 % 싸인을 사용했는데, naver.com으로 끝나는 경우 특정 값을 찾으려고 할 때 사용한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이번엔 ID가 2인 경우의 유저를 지우고 싶다면?","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"DELETE From students WHERE id=2;","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"15~18세 사이의 학생들만 찾고싶다면?","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"SELECT email FROM students WHERE age BETWEEN 15 AND 18"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"이렇게 쉬운데 왜 수많은 개발자들은 SQL을 모르는걸까?"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"그 이유는 ORM 때문이다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"ORM이 뭐냐면 예를들면 파이썬을 가져와서 SQL코드로 바꿔주는 일을 한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그러니까 파이썬으로 코딩하면 ORM 덕분에 SQL코드를 얻는 것이다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그래서인지 ORM은 정말 자주 쓰인다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"파이썬의 경우 장고 ORM이 있고, 라라벨의 경우 eloquent ORM, nodeJS의 경우 Sequelize 혹은 type ORM이 있다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"문제라면 개발자들로 하여금 ORM에 너무 의존하게 만든다."}]}]},{"content":[{"inlineContent":[{"text":"왜냐하면 개발자들은 파이썬 SQL을 바꿔가면서 코딩하기 보단 하나만 가지고 계속 작업하는 것을 좋아하다 보니까 뭔가 안될 때, 더 빠르게 작업해야할 때, 그런 상황이 왔을 때 어떻게 대처할지 모른다는게 문제다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그렇기 때문에 모든 풀스택 개발자들은 SQL을 배워야 한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"왜냐하면 어느순간에는 ORM으로 부족할 시점이 오기 마련이다. 물론 ORM을 쓰지 말란 소리는 아니다.","type":"text"}]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[예습으로 만들었던 CoreData 예제]","type":"text"}]}],"type":"paragraph"},{"code":["class ViewController: UIViewController {"," var context: NSManagedObjectContext { \/\/ 모든 작업을 처리할 수 있는 기본 컨텍스트"," guard let app = UIApplication.shared.delegate as? AppDelegate else {"," fatalError()"," }"," return app.persistentContainer.viewContext"," }"," \/\/ 다만 메인쓰레드에서 작업하기 때문에 블로킹이 발생하지 않도록 주의해야한다.","..."],"type":"codeListing","syntax":"swift"},{"code":["@IBAction func createEntity(_ sender: Any) {"," guard let name = nameField.text,"," let val = ageField.text, let age = Int(val) else {"," return"," }",""," let newEntity = NSEntityDescription.insertNewObject(forEntityName: \"Person\", into: context)"," newEntity.setValue(name, forKey: \"name\")"," newEntity.setValue(age, forKey: \"age\") \/\/ setValue의 value는 Any타입을 받기 때문에 컴파일 시점에서 오류나지 않으니 타입을 잘 확인해야 한다"," \/\/ 저장할 내용이 없는데 세이브를 호출하게 되면 불필요한 리소스를 낭비하게 된다."," \/\/ 그래서 지금처럼 변경사항이 있는지 확인하는 것이 좋다."," if context.hasChanges {"," do {"," try context.save()"," print(\"Saved\")"," } catch {"," print(error.localizedDescription)"," }"," }"," \/\/ 저장후 텍스트필드 초기화"," nameField.text = nil"," ageField.text = nil","}"],"type":"codeListing","syntax":"swift"},{"code":["var editTarger: NSManagedObject?","","@IBAction func readEntity(_ sender: Any) {"," let request = NSFetchRequest(entityName: \"Person\")",""," do {"," let list = try context.fetch(request)"," if let first = list.first {"," nameField.text = first.value(forKey: \"name\") as? String"," if let age = first.value(forKey: \"age\") as? Int {"," ageField.text = \"\\(age)\""," }",""," editTarger = first \/\/ 데이터를 프로퍼티 담아 저장"," } else {"," print(\"Not Found\")"," }"," } catch {",""," }","}"],"syntax":"swift","type":"codeListing"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"readEntity로 저장해둔 editTarget을 수정, 삭제하는 부분"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"codeListing","code":["@IBAction func updateEntity(_ sender: Any) {"," guard let name = nameField.text,"," let val = ageField.text, let age = Int(val) else {"," return"," }",""," if let target = editTarger {"," target.setValue(name, forKey: \"name\")"," target.setValue(age, forKey: \"age\")"," }",""," if context.hasChanges {"," do {"," try context.save()"," print(\"Saved\")"," } catch {"," print(error.localizedDescription)"," }"," }"," \/\/ 저장후 텍스트필드 초기화"," nameField.text = nil"," ageField.text = nil","}"],"syntax":"swift"},{"syntax":"swift","type":"codeListing","code":["@IBAction func deleteEntity(_ sender: Any) {"," if let target = editTarger {"," context.delete(target)",""," if context.hasChanges {"," do {"," try context.save()"," print(\"Saved\")"," } catch {"," print(error.localizedDescription)"," }"," }"," \/\/ 저장후 텍스트필드 초기화"," nameField.text = nil"," ageField.text = nil"," }","}"]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/mackarous.com\/dev\/2018\/12\/4\/dynamic-type-at-any-font-weight"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/ios-development.tistory.com\/344","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uistackview\/2866023-setcustomspacing","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/www.youtube.com\/watch?v=YpVZgQW1TvQ","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/www.youtube.com\/watch?v=55MRdTM8RPE&t=3s","type":"text"}]}]}],"type":"unorderedList"}]}]}]}],"variants":[{"paths":["\/documentation\/arinote\/220124_coredata,_dynamic_type,_uifontmetrics,_setcustomspacing,_viewforzooming,_------,_sql"],"traits":[{"interfaceLanguage":"swift"}]}],"sections":[],"references":{"https://i.imgur.com/Xinny93.png":{"identifier":"https:\/\/i.imgur.com\/Xinny93.png","type":"image","variants":[{"url":"https:\/\/i.imgur.com\/Xinny93.png","traits":["1x","light"]}],"alt":null},"https://i.imgur.com/QIPX0do.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/QIPX0do.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/QIPX0do.png"}],"alt":null},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/SWov102.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/SWov102.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/SWov102.png"}],"alt":null},"https://i.imgur.com/FTcY4Hd.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/FTcY4Hd.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/FTcY4Hd.png"}],"alt":null},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220125_uitapgesturerecognizer,_uicollectionview.json b/docs/data/documentation/arinote/220125_uitapgesturerecognizer,_uicollectionview.json new file mode 100644 index 0000000..55bdf37 --- /dev/null +++ b/docs/data/documentation/arinote/220125_uitapgesturerecognizer,_uicollectionview.json @@ -0,0 +1 @@ +{"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220125_UITapGestureRecognizer,_UICollectionView","interfaceLanguage":"swift"},"metadata":{"roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"title":"220125 UITapGestureRecognizer, UICollectionView","role":"article"},"primaryContentSections":[{"kind":"content","content":[{"level":1,"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"1월 25일 (화)"}]},{"text":"학습 내용","type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"오픈마켓2 STEP 2","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UITapGestureRecognizer + UIScrollView"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"CollectionView의 Cell 포커스를 이동시키기"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2,"text":"고민한 점 \/ 해결 방법"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[2번 탭했을 때 이미지 확대\/축소하기]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"먼저 2번 탭했을 때 메소드를 실행하기 위해 UITapGestureRecognizer를 선언한다."}],"type":"paragraph"}]}]},{"type":"codeListing","syntax":"swift","code":["private lazy var zoomingTap: UITapGestureRecognizer = {"," let tap = UITapGestureRecognizer(target: self, action: #selector(handleZoomingTap))"," tap.numberOfTapsRequired = 2"," return tap","}()"]},{"syntax":"swift","code":["@objc private func handleZoomingTap(_ sender: UITapGestureRecognizer) {"," let location = sender.location(in: sender.view) \/\/ 터치한 부분의 뷰의 좌표를 반환하는 메소드"," zoom(point: location, animated: true) \/\/ 지정한 좌표 부분을 zoom 해주는 메소드 호출","}","","private func zoom(point: CGPoint, animated: Bool) {"," let currectScale = scrollView.zoomScale \/\/ 현재 줌 스케일"," let minScale = scrollView.minimumZoomScale "," let maxScale = scrollView.maximumZoomScale",""," let finalScale = currectScale == minScale ? maxScale : minScale \/\/ 현재 스케일이 minimum이면 확대, 아니라면 축소"," let zoomRect = zoomRect(scale: finalScale, center: point) \/\/ 스케일에 따른 rect를 설정하는 메소드 호출"," scrollView.zoom(to: zoomRect, animated: animated) \/\/ 설정한 rect에 확대, 혹은 축소를 해주는 메소드 호출","}","","private func zoomRect(scale: CGFloat, center: CGPoint) -> CGRect {"," var zoomRect = CGRect.zero"," let bounds = scrollView.bounds \/\/ 스크롤뷰의 bounds를 구한다.",""," \/\/ zoomRect의 크기를 설정"," zoomRect.size.width = bounds.size.width \/ scale"," zoomRect.size.height = bounds.size.height \/ scale",""," \/\/ zoomRect의 위치를 설정"," zoomRect.origin.x = center.x - (zoomRect.size.width \/ 2)"," zoomRect.origin.y = center.y - (zoomRect.size.height \/ 2)"," return zoomRect","}"],"type":"codeListing"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"2번 탭했을 때 Scale, width, height, origin.x, origin.y 모두를 계산하고 설정하는 기능을 구현하여 해당 기능을 구현해주었다."}]},{"items":[{"content":[{"inlineContent":[{"text":"자세한건.. 주석 메모 참고… 맞게 적었는지[?]는 잘모르겠지만, 일단 이해한대로 적어보았다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"좀더 생각해봐야할 부분"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"zoom이 되었을 때 이미지가 스크롤 바깥으로 빠져나가지 못하도록 하는 방법 더 고민해보기"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[지정한 indexPath로 CollectionView를 이동시키기]","type":"text"}]}]},{"type":"codeListing","syntax":"swift","code":["let indexPath = IndexPath(item: self.currentPage, section: 0) \/\/ 현재 페이지에 대한 값을 프로퍼티로 정의하여 활용","collectionView.scrollToItem("," at: indexPath,"," at: [.centeredHorizontally, .centeredVertically], \/\/ 스크롤의 방향을 설정"," animated: false \/\/ 이걸 true로 하면 스크롤 되는게 눈에 보여서 false로 꺼버림",")"]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"scrollToItem 메소드를 활용하여 view가 load되는 시점에 지정한 indexPath로 scroll을 해주는 방식으로 구현하였다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/www.youtube.com\/watch?v=YpVZgQW1TvQ","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uicollectionview\/1618046-scrolltoitem","type":"text"}],"type":"paragraph"}]}]}]}]}]}],"kind":"article","sections":[],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"schemaVersion":{"major":0,"minor":3,"patch":0},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220125_uitapgesturerecognizer,_uicollectionview"]}],"references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220127_uitextfield,_animation,_mvvm,_observable,_uinavigationbarappearance,_appearance.json b/docs/data/documentation/arinote/220127_uitextfield,_animation,_mvvm,_observable,_uinavigationbarappearance,_appearance.json new file mode 100644 index 0000000..72e7442 --- /dev/null +++ b/docs/data/documentation/arinote/220127_uitextfield,_animation,_mvvm,_observable,_uinavigationbarappearance,_appearance.json @@ -0,0 +1 @@ +{"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"schemaVersion":{"major":0,"minor":3,"patch":0},"sections":[],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220127_uitextfield,_animation,_mvvm,_observable,_uinavigationbarappearance,_appearance"]}],"metadata":{"roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}],"title":"220127 UITextField, Animation, MVVM, Observable, UINavigationBarAppearance, Appearance"},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220127_UITextField,_Animation,_MVVM,_Observable,_UINavigationBarAppearance,_Appearance"},"primaryContentSections":[{"kind":"content","content":[{"type":"heading","anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","level":1},{"type":"paragraph","inlineContent":[{"text":"1월 27일 (목)","type":"text"}]},{"level":2,"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"오픈마켓2 STEP 2 피드백","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"UITextField"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"Animation","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"MVVM, Observable"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Navigation Bar 설정 바꿔보기","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Appearance를 항상 Dark로 설정되어있게 하기"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","type":"heading","level":2,"text":"고민한 점 \/ 해결 방법"},{"inlineContent":[{"inlineContent":[{"text":"[UITextField의 입력이벤트를 받아 입력검증하기]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"텍스트필드의 텍스트 값이 변함에 따라 메소드를 실행시켜줄 수가 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"addTarget의 controlEvents중에 UITextField의 경우 allEditingEvents 라는 옵션이 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"이 옵션을 사용하여 실시간으로 사용자의 입력값을 검사하고, 검증되지 않는 값이라면 빨간색 레이블로 사용자에게 입력이 잘못되었음을 알려주도록 할 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"로직은 아래와 같다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["priceTextField.addTarget("," self,"," action: #selector(self.verifyPriceTextField(_:)),"," for: .allEditingEvents"," )"]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"먼저 addTarget을 하여 텍스트필드가 편집중일 때 verifyPriceTextField 메소드가 실행되도록 추가해주었다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"code":["@objc private func verifyPriceTextField(_ sender: Any?) {"," guard let priceText = textFieldsStackView.priceTextField.text else {"," return"," }"," if priceText.count <= .zero {"," DispatchQueue.main.async {"," self.textFieldsStackView.priceTextField.layer.borderColor = UIColor.red.cgColor"," self.textFieldsStackView.priceTextField.layer.borderWidth = 0.5"," self.textFieldsStackView.priceInvalidLabel.isHidden = false"," }"," } else {"," DispatchQueue.main.async {"," self.textFieldsStackView.priceTextField.layer.borderWidth = 0"," self.textFieldsStackView.priceInvalidLabel.isHidden = true"," }"," }","}"],"type":"codeListing","syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 메소드에서는 사용자의 입력값이 없는 경우 기존에 숨겨져있던 “가격을 입력해주세요” 라는 red Color의 label이 나타나며, borderColor에도 색상변경을 주어 입력이 잘못되었음을 알리는 형태이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"만약 사용자가 입력을 제대로 하였다면, 다시 원상태로 복귀하도록 구성되어있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이렇게 controlEvents를 활용하여 기능을 구현해줄 수 있는 방법을 공부해보았다.","type":"text"}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Animation]"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"애니메이션은 Closure 기반으로 작성한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"애니메이션이 실행되는 동안 User Information(터치 등)이 일시적으로 disabled되었다가 끝나면 다시 enable 된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"애니메이션이 가능한 속성이 정해져있다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"좌표, 레이아웃 관련 값"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"frame, bounds, center"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"모양 관련 값","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"transform"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"색상 관련 값"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"alpha, backgroundColor"}]}]}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이렇게 UIView의 몇 가지 속성들을 시작점에서의 현재값과 종료지점을 지정하여 새로운 값으로 변경해주는 것이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIView를 건드리는 것이기 때문에 background 스레드에서는 작업할 수 없다."}]}]}],"type":"unorderedList"},{"code":["class func animate(withDuration duration: TimeInterval,"," delay: TimeInterval,"," options: UIView.AnimationOptions = [],"," animations: @escaping () -> Void,"," completion: ((Bool) -> Void)? = nil)"],"syntax":"swift","type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"withDuration"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"몇 초 동안 애니메이션이 진행될 지 결정한다. 예를 들어 2.0초인 경우 2초동안 애니메이션이 진행된다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"delay"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"몇 초 이후에 시작할 지 딜레이를 결정한다","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"options"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"애니메이션의 옵션을 결정한다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":".allowUserInteraction"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"애니메이션 도중 터치 활성화"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":".repeat","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"애니메이션 무한 반복"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":".autoreverse","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"반대로도 실행가능 (repeat랑 같이 사용해야한다.)","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"아래는 애니메이션의 속도를 지정할 수 있는 옵션이다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":".curveEaseOut","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"끝날 때만 느린 속성"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":".curveEaseIn"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"시작 시에만 느린 옵션"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":".curveEaseInOut","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"시작과 끝이 느린 옵션","type":"text"}],"type":"paragraph"}]}]}]}]}]}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"animations"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"실제로 애니메이션이 될 부분을 정의한다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"frame \/ bounds \/ center","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"뷰의 위치와 크기"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"transform","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"좌표 행렬 값"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"alpha","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"투명도","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"backgroundColor"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"배경색","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"contentStretch"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"확대 \/ 축소 영역","type":"text"}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"hidden과 같이 중간값 계산이 불가능한 속성을 애니메이션이 안된다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"completion","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"애니메이션이 다 종료된 이후 실행되는 부분이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"클로저 형태로 작성이 가능하며, 없다면 nil 값이다."}]}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[CGAffineTransform]","type":"text"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뷰의 프레임을 계산하지 않고 2D 그래픽을 그릴 수 있는 타입이다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"간단하게 사용 가능하기 때문에 자주 사용하곤 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"CGAffineTransform에서 사용되는 아핀 변환 행렬은 2D 그래픽을 그리는데 사용되는 행렬이다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"또한 아핀 변환 행렬은 객체를 회전, 크기 조절, 변환 또는 기울기를 위해 사용된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"아핀 변환을 직접 생성할 필요는 없고 구조체에 있는 함수를 호출해 뷰를 이동(translate), 조절(scale), 회전(rotate)을 한다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"rotationAngle"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뷰를 회전 시킨다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"scaleX"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"뷰의 넓이와 높이를 조정한다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"translationX"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뷰의 위치를 이동시킨다."}]}]}]}]}]}]},{"content":[{"inlineContent":[{"text":"뷰를 원 상태로 복구하려면 CGAffineTransform.identifiy를 호출하면 된다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[animateKeyframes]"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"애니메이션이 끝난 후 다른 애니메이션을 연결하고 싶을때 사용하는 메소드다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"보통 animate의 파라미터 animations 핸들러안에 또 animate를 호출하여 연결하곤 하는데… 이렇게 짜면 가독성이 좋지 못하다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이럴 때 필요한게 animateKeyframes 메소드다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"animate 메소드와 같이 UIView의 타입 메소드이다."}]}]},{"content":[{"inlineContent":[{"text":"현재 View에서 key-frame 기반 애니메이션을 설정하는데 사용할 수 있는 애니메이션 블록 객체를 만드는 역할을 한다.","type":"text"}],"type":"paragraph"}]}]},{"inlineContent":[{"text":"class func animateKeyframes(withDuration duration: TimeInterval,","type":"text"},{"type":"text","text":" "},{"type":"text","text":"delay: TimeInterval,"},{"type":"text","text":" "},{"text":"options: UIView.KeyframeAnimationOptions = [],","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"animations: @escaping () -> Void,"},{"type":"text","text":" "},{"type":"text","text":"completion: ((Bool) -> Void)? = nil)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"withDuration","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"전체 애니메이션의 지속시간이다. 음수 또는 0을 지정하면 애니메이션 없이 즉시 변경된다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"delay"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"애니메이션을 시작하기 전에 대기할 시간을 지정한다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"options"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"애니메이션을 어떻게 수행할지 나타내는 옵션.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"animations","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"view에 커밋할 변경내용이 포함된 블록 객체."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"일반적으로 이 블록 내무에서 addKeyframe 메소드를 호출한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이러한 변경사항을 duration 시간 동안 애니메이션으로 적용하려면, view 값을 직접 변경할 수도 있다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"completion"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"애니메이션이 끝나면 실행되는 블록.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"핸들러가 호출되기 전에 애니메이션이 끝났는지 여부를 나타내는 single boolean argument를 사용한다."}]}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"text":"class func addKeyframe(withRelativeStartTime frameStartTime: Double,","type":"text"},{"type":"text","text":" "},{"type":"text","text":"relativeDuration frameDuration: Double,"},{"type":"text","text":" "},{"type":"text","text":"animations: @escaping () -> Void)"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"withRelativeStartTime"}]},{"items":[{"content":[{"inlineContent":[{"text":"지정된 애니메이션을 시작하는 시간","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 값을 0에서 1까지의 범위여야 하며, 여기서 0은 전체 애니메이션의 시작을 나타내고 1은 전체 애니메이션의 끝을 나타낸다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"예를 들어 애니메이션의 길이가 2초인 경우 시작시간을 0.5로 시작하면 전체애니메이션이 시작된 후 1초 후에 애니메이션이 시작된다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"relativeDuration","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"지정된 값으로 애니메이션을 적용하는데 걸리는 시간이다."}]}]},{"content":[{"inlineContent":[{"text":"이 값은 0~1 범위에 있어야 하며 전체 애니메이션 길이를 기준으로 한 시간의 양(amount of time)의 양을 나타낸다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"값을 0으로 지정하면 애니메이션 블록에서 설정한 모든 속성이 지정된 시작시간에 즉시 업데이트 된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"0이 아닌 값을 지정하면 해당 시간 동안 속성이 애니메이션으로 나타난다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"예를 들어 전체 duration이 2초인 애니메이션의 경우 duration을 0.5로 지정하면 애니메이션 지속 시간이 1초가 된다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"animations","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"수행하려는 애니메이션이 포함된 블록 객체","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[Basic UIView Animation Tutorial]","type":"text"}],"type":"strong"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"튜토리얼을 따라해보며 이해안되는 부분은 다시 복기해가며 공부해보았다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"code":["import UIKit","import AVFoundation","","class ViewController: UIViewController {"," "," @IBOutlet weak var basketTop: UIImageView!"," @IBOutlet weak var basketBottom: UIImageView!"," "," @IBOutlet weak var fabricTop: UIImageView!"," @IBOutlet weak var fabricBottom: UIImageView!"," "," @IBOutlet weak var basketTopConstraint : NSLayoutConstraint!"," @IBOutlet weak var basketBottomConstraint : NSLayoutConstraint!"," "," @IBOutlet weak var bug: UIImageView!"," "," var isBugDead = false"," var tap: UITapGestureRecognizer!"," "," let squishPlayer: AVAudioPlayer \/\/ 사운드 추가를 위한 프로퍼티",""," "," required init?(coder aDecoder: NSCoder) {"," let squishURL = Bundle.main.url(forResource: \"squish\", withExtension: \"caf\")! \/\/ 번들에서 음악파일의 경로를 가져온다."," squishPlayer = try! AVAudioPlayer(contentsOf: squishURL) \/\/ 플레이어를 생성."," squishPlayer.prepareToPlay() \/\/ 재생을 위해 플레이어를 준비한다."," "," super.init(coder: aDecoder)",""," tap = UITapGestureRecognizer(target: self, action: #selector(ViewController.handleTap(_:)))"," }"," "," override func viewDidAppear(_ animated: Bool) {"," openBasket()"," openNapkins()"," moveBugLeft()"," view.addGestureRecognizer(tap)"," }"," "," func openBasket () {"," print(basketTopConstraint.constant, basketBottomConstraint.constant)"," basketTopConstraint.constant -= basketTop.frame.size.height"," basketBottomConstraint.constant -= basketBottom.frame.size.height"," print(basketTopConstraint.constant, basketBottomConstraint.constant)"," "," UIView.animate(withDuration: 0.7, delay: 1.0, options: .curveEaseOut) {"," self.view.layoutIfNeeded()"," } completion: { finished in"," print(\"Basket doors opened!\")"," }"," }"," "," func openNapkins () {"," \/\/ 1.2초 딜레이 후 1초동안 애니메이션을 실행한다. 1초안에 각 프레임을 curveEaseOut(애니메이션이 끝날 때 느려지는 옵션) 옵션으로 animations가 실행된다."," UIView.animate(withDuration: 1.0, delay: 1.2, options: .curveEaseOut) {"," print(self.fabricTop.frame, self.fabricBottom.frame)"," var fabricTopFrame = self.fabricTop.frame"," fabricTopFrame.origin.y -= fabricTopFrame.size.height"," "," var fabricBottomFrame = self.fabricBottom.frame"," fabricBottomFrame.origin.y += fabricBottomFrame.size.height"," "," self.fabricTop.frame = fabricTopFrame"," self.fabricBottom.frame = fabricBottomFrame"," print(fabricTopFrame, fabricBottomFrame)"," } completion: { finished in"," print(\"Napkins opened!\") \/\/ 애니메이션이 다 종료된 이후 실행되는 부분이다."," }"," "," }"," "," func moveBugLeft() {"," if isBugDead { return }"," UIView.animate(withDuration: 1.0,"," delay: 2.0,"," options: [.curveEaseInOut , .allowUserInteraction]) {"," self.bug.center = CGPoint(x: 75, y: 200) \/\/ 뷰의 좌표를 변경한다."," } completion: { finished in"," print(\"Bug moved left!\")"," self.​faceBugRight()"," }"," }"," "," func ​faceBugRight() {"," if isBugDead { return }"," UIView.animate(withDuration: 1.0, delay: 0.0, options: [.curveEaseInOut , .allowUserInteraction]) {"," self.bug.transform = CGAffineTransform(rotationAngle: .pi) \/\/ 뷰를 180도로 회전 시킨다."," } completion: { finished in"," print(\"Bug faced right!\")"," self.​moveBugRight()"," }"," }"," "," func ​moveBugRight() {"," if isBugDead { return }"," UIView.animate(withDuration: 1.0, delay: 2.0, options: [.curveEaseOut, .allowUserInteraction]) {"," self.bug.center = CGPoint(x: self.view.frame.width - 75, y: 250) \/\/ 뷰의 좌표를 변경한다."," } completion: { Boolfinished in"," print(\"Bug moved right!\")"," self.​faceBugLeft()"," }"," }"," "," func ​faceBugLeft() {"," if isBugDead { return }"," UIView.animate(withDuration: 1.0, delay: 0.0, options: [.curveEaseInOut , .allowUserInteraction]) {"," self.bug.transform = CGAffineTransform(rotationAngle: 0.0) \/\/ 뷰를 다시 원래 방향으로 회전 시킨다."," } completion: { finished in"," print(\"Bug faced left!\")"," }"," }"," "," @objc func handleTap(_ gesture: UITapGestureRecognizer) {"," let tapLocation = gesture.location(in: bug.superview) \/\/ 사용자가 탭한 위치를 구한다."," if (bug.layer.presentation()?.frame.contains(tapLocation))! { \/\/ 그 위치가 bug의 위치와 동일하다면..."," print(\"Bug tapped!\")"," if isBugDead { return } \/\/ 버그를 터치하면 애니메이션 체인이 중지되도록 한다."," view.removeGestureRecognizer(tap) \/\/ 더이상 상호작용이 발생하지 않도록 GestureRecognizer에서 제거한다."," isBugDead = true \/\/ 버그가 죽었다..."," squishPlayer.play() \/\/ 버그 죽는 소리..ㅠ"," UIView.animate(withDuration: 0.5, delay: 0.0, \/\/ 새 애니메이션을 시작한다."," options: [.curveEaseOut , .beginFromCurrentState]) {"," self.bug.transform = CGAffineTransform(scaleX: 1.25, y: 0.75) \/\/ 뷰의 넓이와 높이를 조정하여 버그를 찌그러뜨린다."," } completion: { finished in"," UIView.animate(withDuration: 2.0, delay: 0.5, options: []) { \/\/ 버그가 죽어서 사라지는 애니메이션"," self.bug.alpha = 0.0 \/\/ 알파를 0으로 설정해주고... (버그안뇽....)"," } completion: { finished in"," self.bug.removeFromSuperview() \/\/ 버그를 view에서도 지워줌으로써 버그를 죽였다...ㅠ"," }"," }"," } else {"," print(\"Bug not tapped!\")"," }"," }","","}"],"syntax":"swift","type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"느낀점"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"버그의 움직임을 animate의 completion으로 연결해주고 있는데… animateKeyframes를 활용하여 연결해볼 수도 있을 것 같다."}],"type":"paragraph"}]}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[MVVM Data binding - Observable]"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이번 오픈마켓 프로젝트를 하면서 정말 MVC의 단점을 참을 수가 없었다… 첫 설계부터 MVC 구조였기 때문에, 이후 뷰컨이 커져도 어쩌지 못하는 상황이 정말 아쉬웠다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 기회에 MVVM이 어떤거고, 어떻게 사용하는지 한번 배워봐야겠다~~~"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"MVVM 간단요약"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"View는 ViewModel을 가지고 ViewModel은 Model을 가진다."}]}]},{"content":[{"inlineContent":[{"text":"ViewModel은 입출력을 처리하고 UI가 요구하는 로직을 처리하는 역할만 가진다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"VIewModel은 UI를 수정할 수 없다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"MVVM 패턴 정리","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자가 화면에서 액션을 취하면 Command Pattern으로 View에서 ViewModel로 전달된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ViewModel이 Model에게 데이터를 요청하고"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Model은 요청받은 데이터를 통해 업데이트 된 데이터를 ViewModel에게 전달한다."}]}]},{"content":[{"inlineContent":[{"text":"ViewModel은 응답받은 데이터를 가공해서 저장한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"View는 ViewModel과의 Data Binding을 통해서 자동으로 갱신된다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"Command Pattern이란?","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Command = 명령어","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"실행될 기능을 추상화, 캡슐화하여 한 클래스에서 여러기능을 실행할 수 있도록 하는 패턴이다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"Data Binding이란?","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"View와 로직이 분리되어 있어도 한쪽이 바뀌면 다른 쪽도 업데이트가 이루어지는 것을 말한다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"iOS에서 Data Binding을 하는 방법에는 이런 것들이 있다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"KVO"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Delegation","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Functional Reactive Programming","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Property Observer"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"…","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그 중에서도 Observable이라는 패턴이 눈에 띄어서 알아볼 것이다.","type":"text"}]}]}],"type":"unorderedList"},{"syntax":"swift","code":["\/\/ MARK: - Observable","class Observable {"," private var listener: ((T) -> Void)?"," var value: T { \/\/ 값이 set될 때마다 listener를 호출"," didSet {"," listener?(value) \/\/ bind를 통해 클로저를 전달받은 상태이면, value값이 변동이 있을 때마다 해당 클로저를 실행할 것이다."," }"," }"," "," init(_ value: T) { \/\/ [1] value를 저장한다~"," self.value = value"," }"," "," func bind(_ closure: @escaping (T) -> Void) { \/\/ 이 메소드를 호출하게되면 value에 저장했던 값을 전달해준다."," closure(value)"," listener = closure \/\/ listener에게 closure를 전달한다."," }"," ","}","\/\/ MARK: - Model","struct User: Codable {"," let name: String","}","","\/\/ MARK: - ViewModel","","struct UserViewModel {"," var users: Observable<[User]> = Observable([]) \/\/ 모델을 가지고있는 옵저버블.","}","\/\/ MARK: - Controller","","class ViewController: UIViewController {"," "," private var viewModel = UserViewModel()"," "," private let tableView: UITableView = {"," let tableView = UITableView()"," tableView.register(UITableViewCell.self, forCellReuseIdentifier: \"cell\")"," return tableView"," }()",""," override func viewDidLoad() {"," super.viewDidLoad()"," "," view.addSubview(tableView)"," tableView.frame = view.bounds"," tableView.dataSource = self"," "," viewModel.users.bind { [weak self] _ in \/\/ bind 메소드를 통해 listener에 클로저를 전달한다."," DispatchQueue.main.async {"," self?.tableView.reloadData()"," }"," }"," "," fetchData() \/\/ fetchData를 통해 value를 대입해준다."," \/\/ 그러면 이전에 bind 메소드를 통해 reloadData를 전달받은 listener가"," \/\/ value의 값이 변경되는 지점에 호출되어 tableView에 값이 업데이트 될 것이다."," }",""," func fetchData() {"," guard let url = URL(string: \"https:\/\/jsonplaceholder.typicode.com\/users\") else {"," return"," }"," "," let task = URLSession.shared.dataTask(with: url) { data, _, _ in"," guard let data = data else {"," return"," }"," guard let userModels = try? JSONDecoder().decode([User].self, from: data) else {"," return"," }"," self.viewModel.users.value = userModels.compactMap({ newUser in"," User(name: newUser.name) \/\/ 여기에서 value의 값을 새롭게 대입해주고 있다~"," })"," }"," task.resume()"," }","","}","","extension ViewController: UITableViewDataSource {"," func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {"," let cell = tableView.dequeueReusableCell(withIdentifier: \"cell\", for: indexPath)"," cell.textLabel?.text = viewModel.users.value[indexPath.row].name"," return cell"," }"," "," func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {"," return viewModel.users.value.count"," }","}"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"위 예제를 살펴보자면 viewDidLoad에서 viewModel.users.bind를 호출하고, 그 다음에 fetchData를 호출한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"bind를 통해 먼저 Observable의 listener에 클로저를 대입해준다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"내부에 reloadData가 listener에 대입된다고 보면 된다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그 후 fetchData를 살펴보면 네트워크를 통해 data를 가져와서 viewModel.users.value에 대입을 해주고 있는 것을 볼수가 있다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Observable의 value는 프로퍼티 옵저버다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"didSet시 listener를 호출하도록 되어있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 value의 값이 set 될 때 마다, reloadData가 호출된다고 보면 될 것 같다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이런식으로 구현하게 되면 뷰와 뷰모델이 양방향으로 바인딩이 되어있기 때문에 한쪽에 변화가 생기면 다른 한 쪽도 자동으로 업데이트 되게 된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"장점","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"유지보수에 좋다","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"자동화된 테스팅에 적합한 모델이다. (뷰모델과 뷰간의 의존성이 없기 때문)","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"새로운 개발자라도 빠르게 적응이 가능하고 개발이 가능한 수준의 난이도와 복잡성","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"단점","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"단순한 프로젝트를 개발하기에는 MVC에 비해서는 시간이 오래걸린다.","type":"text"}]}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[Navigation Bar 커스텀하기]"}],"type":"strong"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uibarappearance","type":"text"}]}]}],"type":"unorderedList"},{"code":["private func setUpNavigationItem() {"," self.navigationItem.title = \"asdf\""," self.navigationController?.navigationBar.prefersLargeTitles = true"," self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)"," let appearance = UINavigationBarAppearance()"," appearance.configureWithTransparentBackground()"," appearance.backgroundEffect = UIBlurEffect(style: .systemChromeMaterialDark)"," self.navigationController?.navigationBar.standardAppearance = appearance"," self.navigationController?.navigationBar.compactAppearance = appearance ","}"],"type":"codeListing","syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"configureWithTransparentBackground()","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"네비게이션바를 불투명하게 해주는 메소드","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"code":"backgroundEffect","type":"codeVoice"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/uiblureffect\/style"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"여러가지 스타일이 존재한다. 다크모드, 라이트모드로 나누어져 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"`UIBlurEffect(style: .systemChromeMaterialDark)``"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"standardAppearance"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"표준 높이의 설정","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[앱이 항상 다크했음 좋겠따…]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"사용자의 모드가 라이트든 다크든 다 무시하고 항상 다크이고 싶다면…?","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/XxhS8QN.png"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"info.plist에서 Appearance 키를 추가하여 value에 Dark를 주면 된다."}]}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/uiview\/1622451-animate"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/uiview\/animationoptions"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgaffinetransform","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/hyerios.tistory.com\/14"},{"text":" ","type":"text"},{"type":"text","text":"https:\/\/seungchan.tistory.com\/entry\/7%EC%A3%BC%EC%B0%A8-%EC%84%B8%EB%AF%B8%EB%82%98-Animation"},{"text":" ","type":"text"},{"text":"https:\/\/www.youtube.com\/watch?v=iI0LabCYZJo","type":"text"}]}]}],"type":"unorderedList"}]}]}]}],"references":{"https://i.imgur.com/XxhS8QN.png":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/XxhS8QN.png","variants":[{"url":"https:\/\/i.imgur.com\/XxhS8QN.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220128_coredata,_nspredicate,_relationship.json b/docs/data/documentation/arinote/220128_coredata,_nspredicate,_relationship.json new file mode 100644 index 0000000..81b092e --- /dev/null +++ b/docs/data/documentation/arinote/220128_coredata,_nspredicate,_relationship.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"content":[{"level":2,"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"CoreData"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"NSPredicate","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Relationship"}]}]}]}]}]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2,"type":"heading"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[NSPredicate]","type":"text"}],"type":"strong"}]},{"items":[{"content":[{"inlineContent":[{"text":"CoreData를 사용할 때 Predicate 문법을 사용하여 필터링할 때 사용한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Priedicate 키워드들은 대문자로 작성하는 것이 가독성에 좋다.(SQL처럼..)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"문자열 안의 두개 이상의 공백은 하나로 처리된다."}]}]}],"type":"unorderedList"},{"syntax":"swift","code":["\/\/ CoreData에서 특정 id 값 존재","let request: NSFetchRequest = Entity.fetchRequest()","let predicate = NSPredicate(format: \"id == $@\", id)","request.predicate = predicate"],"type":"codeListing"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"패치하기 전에 predicate를 대입해주고 패치를 하게되면, 필터링 한 특정 데이터만 패치되어 데이터를 가져오게 된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSPredicate는 코어데이터 뿐만 아니라 Regular Expression 문법을 따르므로, 핸드폰 번호나 이메일, 패스워드 정규식 체크에도 사용된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSPredicate의 메소드인 evaluate(with:)를 사용하여 정규식 판별을 할 수 있다."}]}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["\/\/ 핸드폰 번호 정규성 체크","func validatePhoneNumber(_ input: String) -> Validation {"," "," let regex = \"^01[0-1, 7][0-9]{7,8}$\""," let phonePredicate = NSPredicate(format: \"SELF MATCHES %@\", regex)"," let isValid = phonePredicate.evaluate(with: input)"," "," if isValid {"," return .valid(input)"," } else {"," return .invalid(\"invalid phone number\")"," }","}","","\/\/ 이메일 정규성 체크","func validateEmail(_ input: String) -> Validation {"," let regex = \"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\\\.[A-Za-z]{2,64}\""," let emailPredicate = NSPredicate(format: \"SELF MATCHES %@\", regex)"," let isValid = emailPredicate.evaluate(with: input)",""," if isValid {"," return .valid(input)"," } else {"," return .invalid(\"invalid email\")"," }","}","","\/\/ 패스워드 정규성 체크","func validatePassword(_ input: String) -> Validation {"," let regex = \"^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[!@#$%^&*()_+=-]).{8,50}\" \/\/ 8자리 ~ 50자리 영어+숫자+특수문자",""," let predicate = NSPredicate(format: \"SELF MATCHES %@\", regex)"," let isValid = predicate.evaluate(with: input)",""," if isValid {"," return .valid(input)"," } else {"," return .invalid(\"invalid password\")"," }","}"]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[Relationship]","type":"text"}],"type":"strong"}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/TuaxX6u.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Relationship을 선택하면 여러가지 옵션들을 설정할 수 있다. 이 옵션들의 의미들은 뭘까?","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"code":"Transient","type":"codeVoice"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"해당 Relationship이 임시적인 값인지 여부를 뜻한다. 여기에 임시적인 속성으로 취급되어 저장소에 들어가지 않는다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"Optional","type":"codeVoice"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 Relationship이 선택적인지 여부를 뜻한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"선택하면 해당 Relationship을 nil로 둘 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"만약 옵셔널이 아닌데 nil로 설정한 뒤 save를 시도하면 런타임 에러가 발생한다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Destination"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Relationship의 타입을 의미한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"자기 자신을 지정할 수도 있다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"code":"Inverse","type":"codeVoice"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"해당 Relationship의 역방향 Relationship을 의미한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"한쪽에서의 변화가 역방향으로도 전파될 수 있도록 모든 Relationship은 Inverse를 가져야 한다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Delete Rules"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"자기 자신이 지워질 때, Relationship으로 연결된 Entity들에게 어떻게 변화가 전파되는 지를 설정한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Inverse가 필수이기 때문에 Destination은 Source 참조를 가지고 있다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"No Action","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"아무런 행동을 하지 않는다. 따라서 Destination에서의 참조는 그대로 유지되며, 이는 수동으로 업데이트 되어야 한다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"Nullify","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Destination의 Source 참조를 nil로 설정한다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"code":"Cascade","type":"codeVoice"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Destination의 객체들을 연쇄적으로 삭제한다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Deny"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"아무런 Destination을 가리키지 않을 때만 삭제가 가능하며 하나라도 다른 객체 참조를 가지고 있으면 삭제가 거부된다."}]}]}]}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Type"}]},{"items":[{"content":[{"inlineContent":[{"text":"해당 Relationship이 1:1 관계인지, 1:N 관계인지를 결정한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"1:N 관계인 경우 최소숫자와 최대 숫자를 정할 수 있다.","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/onelife2live.tistory.com\/35"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/ios-development.tistory.com\/592","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/koggy.tistory.com\/32?category=922104"}]}]}],"type":"unorderedList"}]}]}],"kind":"content"}],"schemaVersion":{"patch":0,"minor":3,"major":0},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"metadata":{"title":"220128 CoreData, NSPredicate, Relationship","roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}]},"kind":"article","variants":[{"paths":["\/documentation\/arinote\/220128_coredata,_nspredicate,_relationship"],"traits":[{"interfaceLanguage":"swift"}]}],"abstract":[{"text":"1월 28일 (금)","type":"text"}],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220128_CoreData,_NSPredicate,_Relationship"},"sections":[],"references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/TuaxX6u.png":{"identifier":"https:\/\/i.imgur.com\/TuaxX6u.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/TuaxX6u.png"}],"type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220207_view_drawing_cycle,_uisplitviewcontroller--.json b/docs/data/documentation/arinote/220207_view_drawing_cycle,_uisplitviewcontroller--.json new file mode 100644 index 0000000..0a9c728 --- /dev/null +++ b/docs/data/documentation/arinote/220207_view_drawing_cycle,_uisplitviewcontroller--.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned","type":"heading","level":1},{"type":"paragraph","inlineContent":[{"text":"2월 7일 (월)","type":"text"}]},{"type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"View Drawing Cycle 활동학습"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"UISplitViewController"}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","level":2},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[View Drawing Cycle]"}],"type":"strong"}]},{"type":"aside","style":"note","content":[{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"Drawing Cycle?"}]},{"text":" ","type":"text"},{"type":"text","text":"뷰가 로드되거나 변경이 있을 때 화면에 시각적으로 표현되어 그려지는 일종의 사이클"}],"type":"paragraph"}],"name":"Note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뷰 로드 시 시스템이 UIView에게 draw 메소드를 통해 드로잉을 요청"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"뷰의 스냅샷을 캡쳐하여 UIView에게 전달"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"뷰의 컨텐츠 변경시 관련 메소드(setNeedsDisplay, setNeedsLayout…)들을 호출하여 시스템에 업데이트 요청","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Next Drawing Cycle에서 업데이트 요청 받은 뷰를 업데이트"}]}]}]},{"inlineContent":[{"type":"text","text":"뷰의 스냅샷을 캡쳐하고 뿌려주는 프로세스를 반복하는 과정"}],"type":"paragraph"},{"level":2,"anchor":"Display","text":"Display","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"draw(_:)","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"CGRect를 전달받아서 뷰를 그리는 메소드","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"호출시점: 뷰의 제약 및 레이아웃이 모두 잡힌 후 호출된다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"setNeedsDisplay()","type":"codeVoice"}]},{"items":[{"content":[{"inlineContent":[{"text":"뷰 내부 요소들을 그려줌","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"호출시점: 드로잉 사이클 시 draw() 호출","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"code":"displayIfNeeded()","type":"codeVoice"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"뷰 내부 요소들을 그려주는데, View Drawing Cycle을 기다리지 않고 즉시 실행한다.","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"level":2,"type":"heading","anchor":"Layout","text":"Layout"},{"items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"layoutSubviews()"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"뷰의 자식뷰들의 위치와 크기를 재조정한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"이 메소드는 재귀적으로 모든 자식뷰의 layoutSubviews까지 호출해야하기 때문에 실행 시에 부하가 큰 메소드이다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"setNeedsLayout()"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뷰의 크기 및 위치를 레이아웃."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"호출시점: 드로잉 사이클 시"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"layoutIfNeeded()"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"뷰의 크기 및 위치를 레이아웃 시킨다. Cycle을 기다리지 않고 즉시 실행된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"layoutSubviews를 호출하도록 하는 메소드이다."}]}]}]}]}],"type":"unorderedList"},{"type":"heading","level":2,"text":"Constraints","anchor":"Constraints"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"code":"updateConstraints()","type":"codeVoice"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"뷰의 제약조건을 업데이트. 동적으로 변해야하는 제약조건을 구현한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"호출시점: 최초 실행시 draw보다 이전."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"setNeedsUpdateConstraints()"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뷰의 제약조건을 업데이트 하도록 예약한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"setNeedsLayout이나 setNeedsDisplay와 비슷하게 작동한다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"updateConstraintsIfNeeded()","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"뷰의 제약조건을 즉시 업데이트 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"layoutIfNeeded 메소드와 유사하다. 그러나 오직 오토레이아웃을 사용하는 뷰에만 유효하다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"invalidateIntrinsicContentSize()"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뷰의 크기가 바뀌었을 때 intrinsicContentSize 프로퍼티를 통해 Size를 갱신하고 그에 맞게 오토레이아웃이 업데이트되도록 만들어주는 메소드다."}]}]}],"type":"unorderedList"}]}]},{"text":"호출순서","type":"heading","anchor":"%ED%98%B8%EC%B6%9C%EC%88%9C%EC%84%9C","level":2},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/7WUpxZI.png","type":"image"}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[SplitView]","type":"text"}]}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Master - Detail 인터페이스를 구현하는 컨테이너 뷰 컨트롤러"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"primary가 마스터, secondary가 디테일"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Container View 중 하나로 계층형 interface에서 자식뷰컨트롤러들을 관리하는 뷰다."}]}]},{"content":[{"inlineContent":[{"text":"인터페이스 내에서 하나의 뷰컨트롤러의 변경 내용을 다른 뷰컨트롤러 드라이브의 내용을 변경한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Split View interface는 Note 앱과 같은 앱에서 content를 navigation 하는 것과 content를 filtering하는 것에 가장 적합한 interface이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"기본 사이드 바에서 폴더를 선택하면 해당 폴더에 있는 노트목록이 표시되고, 목록에 노트를 선택하면 secondary view에서 특정 노트의 내용이 표시가 된다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/fA5qLom.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UI를 빌드할 때 SplitViewController는 앱 화면의 root view controller이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SplitViewController의 경우 고유한 모양은 없다. 대부분의 모양은 내장되는 하위 ViewController에 의해 정의된다."}]}]}]},{"type":"aside","style":"note","name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Note"},{"type":"text","text":" "},{"type":"text","text":"SplitViewController를 네비게이션 스택에 푸쉬할 수 없다. 일부 다른 container View Controller에 하위 뷰컨트롤러로 SplitViewController를 둘 수는 있지만, 대부분의 경우 그렇게 하지 않는 것이 좋다."}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Split View Styles]"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"iOS 14 버전 이후부터는 column-style 레이아웃을 지원한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"적절한 style을 init(style:) 이니셜라이저와 함께 이용하면 두개 혹은 세개의 column을 가진 interface를 생성할 수 있다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UISplitViewController.Style.doubleColumn"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"두개의 열로 이루어진 SplitViewController가 생성된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"primary 및 secondary column에 배치된 두개의 하위 뷰 컨트롤러를 관리한다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"UISplitViewController.Style.tripleColumn"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"세개의 열로 이루어진 SplitViewController가 생성된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"primary, secondary, supplementary column에 배치된 세 개의 하위 뷰 컨트롤러를 관리한다."}]}]}]}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/5R7Ss9m.png","type":"image"}]},{"name":"Note","content":[{"inlineContent":[{"type":"text","text":"iOS 14 이전에는 오직 primary, secondary 뷰컨트롤러를 가진 하나의 SplitViewController만을 지원했는데, 이 인터페이스는 .unspecified 유형이다. iOS14 이후의 column-style API에는 응답하지 않는다."}],"type":"paragraph"}],"style":"note","type":"aside"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[Child View Controllers]"}],"type":"strong"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"column-style split view interface에서 setViewController(_: for:)와viewController(for:) 메서드를 사용하여 각 column의 뷰 컨트롤러들을 지정한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"SplitViewController 같은 경우는 모두 Navigation Controller에 둘러쌓여 있다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"만약 자식 뷰 컨트롤러를 Navigation Controller로 감싸지 않더라도, SplitViewController가 자동으로 Navigation Controller를 감싸준다. ","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SplitViewController는 viewController(for:)를 통해서 original view controller를 반환하지만, 자식 프로퍼티는 뷰 컨트롤러를 wrapping하는데 사용한 navigation controller를 포함한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"view controller를 특정 열에 할당한 후 show("},{"inlineContent":[{"type":"text","text":":) 또는 hide("}],"type":"emphasis"},{"text":":) 메소드를 사용하여 column을 표시화거나 숨길 수 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"기존의 split view 인터페이스에서는 인터페이스 빌더를 사용하거나 프로그래밍 방식으로 뷰 컨트롤러를 viewControllers 프로퍼티에 할당하여 하위 뷰 컨트롤러를 구성할 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"primary 뷰 컨트롤러나 secondary 뷰 컨트롤러를 변경해야하는 경우 show(","type":"text"},{"type":"emphasis","inlineContent":[{"text":":) 및 showDetailViewController(","type":"text"}]},{"text":":sender:) 메소드를 사용하는 것을 권장한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"viewControllers 속성을 직접 수정하는 대신 이러한 방법을 사용하면 SplitViewController가 현재 display mode 및 size class에 가장 적합한 방식으로 지정된 뷰 컨트롤러를 표시할 수 있다.","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Interface Transitions]"}]}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"SplitViewController는 인터페이스의 특정 변경에 따라 축소 및 확장을 수행한다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"예를들어 인터페이스의 size class가 horizontally regular 및 horizontally compact 간에 전환될 때, 혹은 사용자 상호 작용이나 프로그래밍 방식으로 column을 숨기거나 표시할 때 전환이 발생한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SplitViewController는 해당 delegate 객체와 함께 축소 및 확장 전환을 수행한다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"delegate는 UISplitViewControllerDelegate 프로토콜을 채택하는 사용자가 제공하는 객체이다."}]}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"column 스타일 split view 인터페이스에서 인터페이스가 축소될 때 primary, supplementary, secondary 뷰 컨트롤러가 아닌 또 다른 뷰 컨트롤러를 표시할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"setViewController(_:for:) 메소드를 사용하여 UISplitViewController.Column.compact column에 원하는 뷰컨트롤러를 설정하자."}]}]},{"content":[{"inlineContent":[{"text":"축소 및 확장 전환을 커스터마이징 하고 싶다면 ","type":"text"},{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uisplitviewcontrollerdelegate#3596702","type":"reference","isActive":true},{"text":"를 참조하자.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"기존 split view 인터페이스에서 전환을 관리하는 방법에 대한 자세한 내용은 "},{"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uisplitviewcontrollerdelegate#3596701","isActive":true},{"type":"text","text":"를 참조하자."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Display Mode]"}]}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"SplitViewController의 현재 디스플레이 모드는 하위 뷰 컨트롤러의 시각적 배열을 나타낸다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"표시되는 하위 뷰 컨트롤러의 수와 각 컨트롤러가 서로 어떻게 배치되는지를 결정한다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"예를 들어, 하위 뷰 컨트롤러를 한번에 하나씩만 보이도록 나란히 표시하거나, 다른 컨트롤러에 의해 하나가 부분적으로 가려지도록 정렬할 수 있다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"preferredDisplayMode 프로퍼티를 사용하여 기본 디스플레이 모드를 설정한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"SplitViewController는 지정한 디스플레이 모드를 준수하기 위해 노력하지만 공간 제약으로 인해 해당모드를 시각적으로 수용하지 못할 수 있다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"예를 들어 SplitViewController는 수평으로 압축된 환경에서 하위 뷰 컨트롤러를 나란히 표시할 수 없다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"For possible configurations, see UISplitViewController.DisplayMode.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/iwbDGGX.png"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"preferredDisplayMode를 설정한 후 SplitViewController는 자체 업데이트 하고 displayMode 프로퍼티에 반영된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"만약 어떤 column이 보여지기를 원하는지 변경하고 싶다면, show(","type":"text"},{"inlineContent":[{"type":"text","text":":) 혹은 hide("}],"type":"emphasis"},{"type":"text","text":":) 메소드를 사용하면 된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"SplitViewController는 원하는 column을 표시하도록 display mode를 업데이트 하는 방법을 결정할 것이다.","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[Gesture and Button Support]"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자 상호작용으로 현재 디스플레이 모드를 변경할 수 있는 여러가지 방법이 있다."}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"SplitViewController 내부에는 gesture recognizer가 포함되어 있는데, 이는 스와이프를 통해서 유저가 디스플레이 모드를 변경할 수 있도록 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 스와이프를 presentsWithGesture 프로퍼티를 false 값으로 변경함으로써 제어할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"만약 presentsWithGesture 프로퍼티가 false라면 우리는 primary view controller를 항상 볼 수 있을 것이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그러나 true라면 SplitViewController는 디스플레이 모드를 바꾸는 가지고 있을 것이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"SplitViewController는 이 버튼의 행동(behavior), 모양(appearance), 위치(positioning) 까지도 관리해줄 수 있다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"UISplitViewController는 사이드바 토글 아이콘으로써 사용되는 UISplitViewController.SplitBehavior.title과 back-chevron 아이콘으로써 사용되는 UISplitViewController.SplitBehavior.overlay와 UISplitViewController.SplitBehavior.displace를 나타낸다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이 버튼을 누르면 현재 디스플레이 모드 및 split view의 동작에 따라 새로운 디스플레이 모드로 전환된다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"3-column의 split view interface - UISplitViewController.Style.tripleColumn의 경우 디스플레이 모드에 영향을 미치는 또다른 property는 showsSecondaryOnlyButton이다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이 프로퍼티가 참이면 SplitViewController는 UISplitViewController.DisplayMode.secondaryOnly에서 혹은 해당 mode로 변경하기 위한 toggle bar button item을 만든다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SplitViewController는 behavior, appearance 그리고 item의 위치를 관리한다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이 아이콘은 이중 화살표 아이콘이다. 사용자가 이 버튼을 누르면 디스플레이 모드가 secondaryOnly로 전환된다.","type":"text"}]}]}],"type":"unorderedList"}]}]},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[Split Behavior]"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"SplitViewController에서 split behavior은 secondary view controller가 다른 view controller와 비교하여 나타나지는 방식을 제어한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"secondary view controller가 side에서 다른 view controller에 비해 분명하게 나타나게 할 것인지, 혹은 어둡게 하여 스크린에서 잘 안보이게 할 지를 설정할 수 있다.","type":"text"}],"type":"paragraph"}]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/XzZlJgf.png","type":"image"}],"type":"paragraph"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/lazyowl.tistory.com\/entry\/iOS-%EB%A0%88%EC%9D%B4%EC%95%84%EC%9B%83-%EC%82%AC%EC%9D%B4%ED%81%B4-%EB%B0%8F-%EB%93%9C%EB%A1%9C%EC%9E%89-%EC%82%AC%EC%9D%B4%ED%81%B4"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/green1229.tistory.com\/67"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/baked-corn.tistory.com\/105"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/magi82.github.io\/ios-intrinsicContentSize\/"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@elile-e\/Drawing-Cycle"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/uisplitviewcontroller"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/kyungpyoda.tistory.com\/entry\/%EB%B2%88%EC%97%AD-UISplitViewController?category=890396","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@leeyoungwoozz\/UISplitViewController"}]}]}]}]}],"type":"unorderedList"}]}],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220207_view_drawing_cycle,_uisplitviewcontroller--"]}],"schemaVersion":{"major":0,"minor":3,"patch":0},"metadata":{"roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}],"title":"220207 View Drawing Cycle, UISplitViewController이론"},"sections":[],"kind":"article","identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220207_View_Drawing_Cycle,_UISplitViewController--"},"references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/7WUpxZI.png":{"alt":null,"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/7WUpxZI.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/7WUpxZI.png"},"https://i.imgur.com/XzZlJgf.png":{"alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/XzZlJgf.png"}],"identifier":"https:\/\/i.imgur.com\/XzZlJgf.png"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/fA5qLom.png":{"alt":null,"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/fA5qLom.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/fA5qLom.png"},"https://i.imgur.com/iwbDGGX.png":{"alt":null,"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/iwbDGGX.png"}],"identifier":"https:\/\/i.imgur.com\/iwbDGGX.png"},"https://i.imgur.com/5R7Ss9m.png":{"alt":null,"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/5R7Ss9m.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/5R7Ss9m.png"},"https://developer.apple.com/documentation/uikit/uisplitviewcontrollerdelegate#3596701":{"type":"link","titleInlineContent":[{"text":"Classic Split Views","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uisplitviewcontrollerdelegate#3596701","title":"Classic Split Views","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uisplitviewcontrollerdelegate#3596701"},"https://developer.apple.com/documentation/uikit/uisplitviewcontrollerdelegate#3596702":{"type":"link","titleInlineContent":[{"type":"text","text":"Column-Style Split Views"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uisplitviewcontrollerdelegate#3596702","title":"Column-Style Split Views","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uisplitviewcontrollerdelegate#3596702"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220208_reloadrows,_cancelstouchesinview,_translatesautoresizingmaskintoconstraints,_array_safe_access,_barbuttonsystemitem,_uisplitviewcontrollerdelegate.json b/docs/data/documentation/arinote/220208_reloadrows,_cancelstouchesinview,_translatesautoresizingmaskintoconstraints,_array_safe_access,_barbuttonsystemitem,_uisplitviewcontrollerdelegate.json new file mode 100644 index 0000000..d7987ee --- /dev/null +++ b/docs/data/documentation/arinote/220208_reloadrows,_cancelstouchesinview,_translatesautoresizingmaskintoconstraints,_array_safe_access,_barbuttonsystemitem,_uisplitviewcontrollerdelegate.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","level":1,"type":"heading","anchor":"TIL-Today-I-Learned"},{"inlineContent":[{"type":"text","text":"2월 8일 (화)"}],"type":"paragraph"},{"level":2,"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"특정 행에 해당되는 셀만 업데이트 하는 방법"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"GestureRecognizer를 추가했을 때 tableView Delegate가 먹통인 이유"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"translatesAutoresizingMaskIntoConstraints 복습","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"배열의 인덱스 안전하게 조회하는 방법"}]}]},{"content":[{"inlineContent":[{"text":"barButtonSystemItem","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"SplitViewController 인터페이스가 축소되었을 때 primary가 보여지게 설정하기"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"level":2,"text":"고민한 점 \/ 해결 방법","type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[특정 행에 해당되는 셀을 업데이트를 해주는 방법]"}]}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"reloadRows(at:with:) 메소드를 활용하여 indexPath를 할당해주면, 그에 맞는 cell만 reload를 실행한다."}]}]}],"type":"unorderedList"},{"syntax":"swift","type":"codeListing","code":["\/\/ 사용 예시","func updateData(at index: Int, with data: MemoListInfo) {"," memoListInfo[index] = data"," tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .none)","}"]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Cell의 Select가 먹히는 문제 해결]"}]}]},{"syntax":"swift","type":"codeListing","code":["extension UIViewController {"," func hideKeyboard() {"," let tap = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))"," view.addGestureRecognizer(tap)"," tap.cancelsTouchesInView = false \/\/ 이거 추가하니까 해결댐..."," }",""," @objc private func dismissKeyboard() {"," view.endEditing(true)"," }","}"]},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[cancelsTouchesInView]","type":"text"}],"type":"strong"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Bool 타입의 프로퍼티로 default 값은 true이다."}]}]},{"content":[{"inlineContent":[{"text":"Gesture Recognizer가 제스처를 인식하면 나머지 터치정보들을 뷰로 전달하지 않고 이전에 전달된 터치들은 취소된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"하지만 false로 할당한다면 제스처를 인식한 후에도 터치 정보를 뷰에 전달하게 된다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"aside","name":"Note","content":[{"inlineContent":[{"inlineContent":[{"text":"문제가 해결된 이유","type":"text"}],"type":"strong"},{"text":" ","type":"text"},{"text":"기존에는 cancelsTouchesInView값이 true여서 나머지 터치정보들을 뷰로 전달하지 않고 취소되었기 때문에 TableView의 Select가 먹지 않았던 것이였다. false로 할당해줌으로써 제스처를 인식한 후에도 Gesture Recognizer의 패턴과는 무관하게 터치 정보를 뷰에 전달하게 되어 이 문제가 해결되었던 것이다.","type":"text"}],"type":"paragraph"}],"style":"note"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[translatesAutoresizingMaskIntoConstraints]","type":"text"}],"type":"strong"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"까묵어서.. 다시 복기하기…"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"autoresizingMask는 superview가 변함에 따라 subview의 크기를 어떻게 할것인가이기 때문에, 이와 동일한 기능을 하는 autolayout에서 같이 사용된다면 충돌이 날 수 있는것 > 충돌 방지를 위해 Auturesizing을 사용하지 않는것으로 명시적으로 false를 할당해주는 것이다."}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"text":"[배열의 인덱스를 안전하게 조회하는 방법]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"메모장 앱 구현시 존재하지 않는 인덱스 조회로 인해 앱이 죽어버리는 현상이 발생되어 알아보게 되었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"subscript를 추가하여 Collection에 extension을 해주는 방법이다.","type":"text"}]}]}]},{"syntax":"swift","code":["extension Collection {"," subscript (safe index: Index) -> Element? {"," return indices.contains(index) ? self[index] : nil"," }","}"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"배열에 접근하려는 인덱스가 유효한지 판단한 뒤 유효할 경우 Element를 반환하고 아닌 경우 nil을 반환하는 메소드이다."}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[UIBarButtonItem은 기본 구현되어있는 버튼아이콘이 존재한다.]"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"HIG에 아주 친절하게 표로 안내해주고 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/6cL2pyo.jpg"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/icons-and-images\/system-icons\/","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"코드로는 아래처럼 접근할 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["UIBarButtonItem(barButtonSystemItem: .add, target: self, action: nil)"]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[SplitViewController 인터페이스가 축소되었을 때 primary가 보여지게 설정하기]","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"아이패드에서 스플릿뷰로 다른 앱과 화면을 같이 쓰는 경우 화면이 작아져서 primary와 secondary뷰가 한번에 보이지 않았다. primary뷰인 메모목록이 먼저 보여지게 하고 싶었는데 secondary뷰인 메모장이 먼저 보여지는 현상이 발생하였다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"디폴트 값이 secondary뷰임을 확인하고 primary가 먼저 보여지도록 delegate 메서드를 통해 설정해주었다."}],"type":"paragraph"}]}]},{"type":"codeListing","code":["extension SplitViewController: UISplitViewControllerDelegate {"," func splitViewController("," _ svc: UISplitViewController,"," topColumnForCollapsingToProposedTopColumn proposedTopColumn: UISplitViewController.Column"," ) -> UISplitViewController.Column {"," return .primary"," }","}"],"syntax":"swift"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/yojkim.medium.com\/swift%EC%97%90%EC%84%9C-%EC%A2%80-%EB%8D%94-%EC%95%88%EC%A0%84%ED%95%9C-%EB%B0%A9%EB%B2%95%EC%9C%BC%EB%A1%9C-%EB%B0%B0%EC%97%B4%EC%97%90-%EC%A0%91%EA%B7%BC%ED%95%98%EA%B8%B0-47605e7a192b","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/baked-corn.tistory.com\/130"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/violentjy.tistory.com\/122"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/design\/human-interface-guidelines\/ios\/icons-and-images\/system-icons"},{"text":" ","type":"text"},{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/uisplitviewcontroller"}]}]}]}]}]}]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220208_reloadrows,_cancelstouchesinview,_translatesautoresizingmaskintoconstraints,_array_safe_access,_barbuttonsystemitem,_uisplitviewcontrollerdelegate"]}],"sections":[],"schemaVersion":{"minor":3,"patch":0,"major":0},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220208_reloadRows,_cancelsTouchesInView,_translatesAutoresizingMaskIntoConstraints,_Array_Safe_Access,_barButtonSystemItem,_UISplitViewControllerDelegate"},"metadata":{"roleHeading":"Article","role":"article","title":"220208 reloadRows, cancelsTouchesInView, translatesAutoresizingMaskIntoConstraints, Array Safe Access, barButtonSystemItem, UISplitViewControllerDelegate","modules":[{"name":"Ari's Notes"}]},"kind":"article","references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/6cL2pyo.jpg":{"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/6cL2pyo.jpg","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/6cL2pyo.jpg","alt":null},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220210_core_graphics,_codegen,_returnsobjectsasfaults,_nsfetchrequestresult,_array_item_to_new_position.json b/docs/data/documentation/arinote/220210_core_graphics,_codegen,_returnsobjectsasfaults,_nsfetchrequestresult,_array_item_to_new_position.json new file mode 100644 index 0000000..43ef0a2 --- /dev/null +++ b/docs/data/documentation/arinote/220210_core_graphics,_codegen,_returnsobjectsasfaults,_nsfetchrequestresult,_array_item_to_new_position.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"variants":[{"paths":["\/documentation\/arinote\/220210_core_graphics,_codegen,_returnsobjectsasfaults,_nsfetchrequestresult,_array_item_to_new_position"],"traits":[{"interfaceLanguage":"swift"}]}],"sections":[],"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"220210 Core Graphics, codegen, returnsObjectsAsFaults, NSFetchRequestResult, Array item to new position."},"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","level":1,"type":"heading","anchor":"TIL-Today-I-Learned"},{"inlineContent":[{"type":"text","text":"2월 10일 (목)"}],"type":"paragraph"},{"type":"heading","text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Core Graphics","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"활동학습"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Core Data: ","type":"text"},{"type":"codeVoice","code":"codegen"},{"type":"text","text":"이 뭔데…?"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"NSFetchRequest - returnsObjectsAsFaults"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Protocol - NSFetchRequestResult"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"동기화 메모장 프로젝트 문제해결","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Array - extension ("},{"code":"move(from:to:)","type":"codeVoice"},{"type":"text","text":")"}]}]}],"type":"unorderedList"}]}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","type":"heading","text":"고민한 점 \/ 해결 방법","level":2},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[Core Graphics]"}],"type":"strong"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/library\/archive\/documentation\/GraphicsImaging\/Conceptual\/drawingwithquartz2d\/Introduction\/Introduction.html#\/\/apple_ref\/doc\/uid\/TP40007533-SW1"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Quartz 2D","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"iOS나 Mac OSX 어플리케이션 환경에서 접근할 수 있는 Drawing Engine으로써 2차원 그래픽을 그릴 때 사용된다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Quartz 2D API를 통해서 좌표를 기준으로 그래픽 그리기, 투명도 조절, 그림자, 명암, 레이어의 투명도 등의 그래픽 특징을 조절할 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Quartz 2D가 사용될 때는 언제든 그래픽 하드웨어의 도움을 받게 된다."}]}]},{"content":[{"inlineContent":[{"text":"2차원의 어떤 것들을 그리기 위한 엔진","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"iOS, tvOS, macOS의 2차원 그림을 그리는 개발도구엔진","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Mac OS X에서 Quartz 2D는 다른 모든 그래픽 및 이미징 기술(Core Image, Core Video, OpenGL 및 QuickTime)과 함께 이용할 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"iOS에서 Quartz 2D는 Core Animation, OpenGL ES 및 UIKit 클래스와 같은 사용 가능한 모든 그래픽 및 애니메이션 기술과 함께 이용할 수 있다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Page","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Quartz 2D는 이미지를 그리는데 painter’s model을 사용한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"painter’s model에서 성공적으로 완성된 그리기 작업은 하나의 paint레이어를 Page라 불리는 출력용 캔버스에 적용시킨다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"페이지 위에 paint는 다른 그리기 작업을 시행하여 그위에 다른 paint를 overlaying하는 방식으로 수정할 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"한번 그려진 페인트는 이처럼 페인트를 오버레이어하는 방식 이외에는 수정할 방법이 없다."}]}]},{"content":[{"inlineContent":[{"text":"아래 그림은 두가지 다른 형태의 그림을 그릴 때 순서의 중요성에 대해 알 수 있는 그림이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"앞서 그린 것이 깔리게되고, 그 뒤에 그린 것이 겹쳐지게 되는 것을 설명한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/5xX9tAK.gif","type":"image"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"Graphics Context","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"opaque data type","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontextref","isActive":true},{"text":"는 이미지를 그리는데 사용하는 정보를 캡슐화한 불투명한 데이터 타입이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Quartz를 통하여 그려진 모든 이미지는 output device 즉 PDF, bitmap, window display에 사용되기 위하여 그려진다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"어떤 디바이스든 사용할 수 있도록 캡슐화된 형태로 생각하면 될 것 같다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/KvpeEAa.gif","type":"image"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 Quartz가 알아서 디바이스 환경에 따라서 만들어 줄 것이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"위 그림처럼 drawing destination에 따라 여러가지 형태의 graphics context로 나타내질 수 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"graphics context는 bitmap graphics context, PDF graphics context, window graphics context 등 여러가지 종류가 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"그 중 iOS는 View graphics context를 사용한다.","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[Quartz 2D Coordinate Systems]","type":"text"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Quartz는 그래픽을 생성할 때 고유의 coordinate system, 즉 좌표계를 통해 위치와 사이즈를 나타낸다.","type":"text"}]}]},{"content":[{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/Be2uB1E.gif","type":"image"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Quartz의 좌표는 좌측 아래가 0.0이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"그래픽을 출력하게되는 기기마다 디스플레이의 크기가 다르기 때문에 그래픽의 좌표를 디바이스 단에서 설정하게되면 기기마다 다르게 설정해줘야하는 번거로움이 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"때문에 Quartz는 그래픽을 디바이스와 독립된 user space에 좌표계를 생성하고 각 기기의 device space 좌표로 current transformation matrix나 CTM을 통하여 맵핑을 시킨다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"current transformation matrix라는 일종의 좌표를 이동(평행이동, 회전 등)시킬 수 있는 행렬을 좌표에 곱하여 기존의 좌표를 이동시킨다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 그려질 좌표는 인식하되 실질적으로 그려지기 전에 기존의 Quartz 좌표계에 보이는 이미지를 그리는 것이 아닌 각 디바이스에 그려질 이미지로 변환하여 그려지게 된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIKit도 고유의 좌표계를 가지기 때문에 마찬가지로 Quartz의 좌표계에 그려질 그래픽을 UiKit 좌표계에 맞게 변형시킨 drawing contexts인 UIView를 활용하게 된다."}]}]},{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/33HbCPg.jpg"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"위 그래프와 같이 UiKit을 통하여 그려질 때는 Quartz 좌표계에서 y축의 양의 방향이 반대로 되어 좌표를 인식하게 된다."}],"type":"paragraph"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이외에도 문서에는 Quartz의 graphics context에 따라 사용되는 Opaque Data Types, Quartz의 메모리 관리 방식, Quartz를 통해 완성된 객체를 수정할 수 있는 Graphics States에 관한 설명이 있다.","type":"text"}]}],"style":"note","type":"aside","name":"Note"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[UIGraphicsGetCurrentContext]","type":"text"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"code":"UIGraphicsGetCurrentContext","type":"codeVoice"},{"text":"를 이용해서 View 위에 원을 그려보자~","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"타입에 "},{"code":"@IBDesignable","type":"codeVoice"},{"type":"text","text":" 옵션을 준 후 원하는 속성(선 굵기, 컬러 등)에 "},{"type":"codeVoice","code":"@IBInspectable"},{"text":" 속성을 붙여주어 스토리보드를 적극 활용하면 해당지식을 입문하기 좋은 것 같다.","type":"text"}]}]}]}]}]},{"style":"note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"먼저 UIView를 상속하는 커스텀 뷰를 생성해서 draw 메소드를 오버라이드 한다. 아래 예제는 UIButton을 Custom 타입으로 생성하였다."}]}],"name":"Note","type":"aside"},{"type":"codeListing","syntax":"swift","code":["override func draw(_ rect: CGRect) {","","}"]},{"type":"aside","content":[{"inlineContent":[{"text":"그리고 context 인스턴스를 생성해준다.","type":"text"}],"type":"paragraph"}],"name":"Note","style":"note"},{"type":"codeListing","code":["override func draw(_ rect: CGRect) {"," guard let context = UIGraphicsGetCurrentContext() else {"," return"," }","}"],"syntax":"swift"},{"content":[{"type":"paragraph","inlineContent":[{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontext\/1456635-beginpath"},{"type":"text","text":" 메소드를 호출해서 새 경로를 추가해주자."}]}],"name":"Note","style":"note","type":"aside"},{"code":["override func draw(_ rect: CGRect) {"," guard let context = UIGraphicsGetCurrentContext() else {"," return"," }"," context.beginPath() \/\/ 나 그림 그릴거야~~ 알려주기","}"],"syntax":"swift","type":"codeListing"},{"content":[{"inlineContent":[{"type":"text","text":"먼저 원 형태의 도형을 그려볼 것이다. 그 전에 원의 Rect를 정의해보자."},{"type":"text","text":" "},{"text":"UIButton을 꽉 채우도록 bounds를 활용하여 설정해주었다. ","type":"text"},{"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgrect\/1454218-insetby","isActive":true}],"type":"paragraph"}],"style":"note","type":"aside","name":"Note"},{"type":"codeListing","code":["let width = bounds.width","let height = bounds.height","","let circleRect = bounds.insetBy(dx: width * 0.05, dy: height * 0.05) \/\/ 좌표를 지정하면 CFRect을 반환한다."],"syntax":"swift"},{"type":"aside","style":"note","name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"선의 너비와, 채워줄 색상을 설정한다. "},{"identifier":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontext\/1455270-setlinewidth","isActive":true,"type":"reference"},{"type":"text","text":" "},{"isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontext\/1454079-setfillcolor","type":"reference"}]}]},{"syntax":"swift","code":["override func draw(_ rect: CGRect) {"," guard let context = UIGraphicsGetCurrentContext() else {"," return"," }"," context.beginPath() \/\/ 나 그림 그릴거야~~ 알려주기"," context.setLineWidth(lineWidth) \/\/ 선 굵기 지정"," context.setFillColor(fillColor.cgColor)"," \/\/ @IBInspectable var fillColor: UIColor = .systemBackground","}"],"type":"codeListing"},{"content":[{"type":"paragraph","inlineContent":[{"text":"그리고 아까 정의해두었던 CGRect를 활용하여 원을 그려준다. ","type":"text"},{"isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontext\/1456420-addellipse","type":"reference"}]}],"name":"Note","type":"aside","style":"note"},{"code":["override func draw(_ rect: CGRect) {"," guard let context = UIGraphicsGetCurrentContext() else {"," ..."," "," context.addEllipse(in: circleRect)"],"syntax":"swift","type":"codeListing"},{"style":"note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"원 그리기 완성!"},{"text":" ","type":"text"},{"identifier":"https:\/\/i.imgur.com\/R2S3Ydm.png","type":"image"}]}],"name":"Note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"CGContext 공식문서를 보면 그림 그리기 위한 많은 메소드가 존재한다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontext","type":"text"}]}]}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[Codegen]","type":"text"}]}]},{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/coredata\/modeling_data\/generating_code","type":"text"}],"type":"paragraph"},{"style":"note","type":"aside","name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로젝트 하는 도중에 codegen을 어떤 옵션으로 줘야할지 감이 안와서 찾아보았다."}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 entity에 대한 클래스 선언을 자동으로 만들어 주는 옵션을 설정합니다."}]},{"items":[{"content":[{"inlineContent":[{"text":"None\/Manual: 관련 파일을 자동으로 만들어주지 않는다. 개발자는 DataModel을 선택한 상태에서 Editor-Create NSManagedObject Subclass 항목을 클릭하여 클래스 선언 파일과 프로퍼티 extension 파일을 빌드시마다 추가시켜 주고, 이를 수동으로 관리해야 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Class Definition: 클래스 선언 파일과 프로퍼티 관련 extension 파일을 빌드시마다 자동으로 추가시켜준다. 따라서 관련된 파일을 전혀 추가시켜줄 필요가 없다.(그래서도 안된다. 만약 수동으로 추가시켜준 상태에서 빌드를 시도하면 컴파일 에러가 발생한다.)","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Category\/Extension: 프로퍼티 관련 extension파일만 자동으로 추가시켜 준다. 즉, 클래스 선언에는 사용자가 원하는 로직을 자유롭게 추가할 수 있다."}]}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[NSFetchRequest - returnsObjectsAsFaults]","type":"text"}]}]},{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/coredata\/nsfetchrequest\/1506756-returnsobjectsasfaults"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"CoreData을 관리하는 모델을 설계하다가 이런 프로퍼티를 발견하게 되었다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"기본값은 true인 속성이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"true인 경우 Request로 가져온 객체가 Faulting인 경우라고 한다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/library\/archive\/documentation\/Cocoa\/Conceptual\/CoreData\/FaultingandUniquing.html","type":"text"}]}]}]}]}],"type":"unorderedList"},{"type":"aside","name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Faulting 예시"},{"text":" ","type":"text"},{"type":"image","identifier":"https:\/\/i.imgur.com\/ZbWKJo1.png"}]}],"style":"note"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"사진과 같이 Department는 클래스 인스턴스이다. 그리고 인스턴스가 생성되어있지만, 속성은 비어있는 상태이다. 이 상태를 "},{"type":"codeVoice","code":"결함이 있다"},{"text":"라고 본다는 것이다. (이를 오류라고 함) 부서가 존재하지 않으니 직원 인스턴스도 생성할 필요가 없을 뿐만 아니라 관계도 채울필요도 없음을 의미한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그래프가 완전 해야 하는 경우 직원의 프로퍼티를 편집하려면 궁극적으로 전체 기업 구조를 나타내는 개체를 만들어야한다."}]}]}],"type":"unorderedList"},{"style":"note","name":"Note","type":"aside","content":[{"inlineContent":[{"type":"text","text":"따라서 returnsObjectsAsFaults가 true인 경우 위와 같은 결함을 가지고있는 경우에도 위 그림과 같은 Department 인스턴스를 생성하지않는다는 이야기인 것 같다. 즉 결함을 허용하겠다는 의미인걸까? false인 경우에는 결함이 있던 말던 모든 인스턴스를 반환하도록 강제한다는 뜻인 것 같다."}],"type":"paragraph"}]},{"items":[{"content":[{"inlineContent":[{"text":"뭔 말인지 잘 이해가 가지 않아서 좀더 공부가 필요할 것 같다…","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"중요한 것은 returnsObjectsAsFaults 이 플래그가 CoreData에 매우 메모리 효율적인 Lazy loading를 수행하도록 지시한다고 한다. [?]"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"메모리 최적화","type":"codeVoice"},{"text":"랑 연관이 있다고…","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/ali-akhtar.medium.com\/mastering-in-coredata-part-10-nsfetchrequest-a011684dd8f7"}]}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[Protocol - NSFetchRequestResult]","type":"text"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/coredata\/nsfetchrequestresult"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"FetchRequest를 보낼때 단순히 NSManagedObject말고 다른 타입들도 유연하게 받고싶다면, 이 프로토콜을 활용할 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Conforming Types"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSDictionary"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"NSManagedObject"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSManagedObjectID"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"NSNumber"}]}]}]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[동기화 메모장 프로젝트]"}]}],"type":"paragraph"},{"name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"혼자 코드 끄적이다가… 몇가지 해결한 문제점들을 기록해본다"}]}],"type":"aside","style":"note"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"textViewDidChange를 할때마다 코어데이터에 createMemo 메소드로 NSManagedObject를 생성하고 있는 부분 때문에 ’+’버튼을 누를 경우 글자를 입력한 순서대로 와장창… 메모가 추가된다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"예시) 텍스트 “asd”를 순서대로 입력하고 ","type":"text"},{"type":"codeVoice","code":"'+' 버튼 클릭시"},{"type":"text","text":" 메모 a, 메모 s, 메모 d 총 3개의 메모와 새로운 메모, 즉 "},{"code":"총 4개의 메모장","type":"codeVoice"},{"type":"text","text":"이 생성된다아…"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"코어데이터에 생성한 엔티티에 ","type":"text"},{"type":"codeVoice","code":"고유한 식별자"},{"text":"가 없다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"기존 메모를 수정해주려먼 해당 메모를 "},{"type":"codeVoice","code":"NSPredicate로 필터링해서 fetch한 후"},{"type":"text","text":" "},{"type":"codeVoice","code":"수정"},{"type":"text","text":"을 해주어야 하는데, 고유한 식별자가 없어서 불가능하다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뷰에 보여줄 요소들을 별도의 타입으로 만들어준 부분(MemoListInfo, MemoDetailInfo)은 필요없다고 판단되었다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"데이터를 가공해서 인스턴스를 만드는 작업까지 꽤나 많은 양의 코드가 들어가있다. 모두 제거해주고 3개의 ViewController에서 Memo 타입만을 사용하게 된다면 코드 가독성이 올라갈 것 같다.","type":"text"}],"type":"paragraph"}]}]}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[문제해결을 하면서 알게된 부분]"}]}]},{"items":[{"content":[{"inlineContent":[{"text":"날짜가 최신이면 리스트에서 최상단으로 cell이 move했으면 좋겠어!","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableview\/1614987-moverow"}]}]}]}]}],"type":"unorderedList"},{"name":"Note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Cell이 이동하는 동시에 데이터도 처리가 필요해서 Array의 요소를 새 인덱스로 이동하는 기능이 필요한데…"}]}],"style":"note"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"뭐 구글링하니까 누가 좋은 코드를 써두었네 ㅎㅎ","type":"text"}],"type":"paragraph"}]}]},{"type":"codeListing","syntax":"swift","code":["\/\/ 배열의 요소를 새 인덱스로 이동시키 기능 확장","extension Array {"," mutating func move(from oldIndex: Index, to newIndex: Index) {"," guard oldIndex != newIndex else { \/\/ 두 인덱스가 같으면 탈출"," return"," }"," if abs(newIndex - oldIndex) == 1 { \/\/ 절대값을 계산해서 1이라면"," return self.swapAt(oldIndex, newIndex) \/\/ 스왑해서 리턴"," }"," self.insert(self.remove(at: oldIndex), at: newIndex) \/\/ insert를 이용하네.. 지웠다가 새인덱스에 추가함"," }","}",""]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/moonibot.tistory.com\/52"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/moonibot.tistory.com\/48"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/jcsoohwancho.github.io\/2020-01-02-Core-Data-%EC%8B%9C%EC%9E%91%ED%95%98%EA%B8%B0(2)-Data-Model-%EB%A7%8C%EB%93%A4%EA%B8%B0(1)-entity%EB%A7%8C%EB%93%A4%EA%B8%B0\/","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/stackoverflow.com\/questions\/34545708\/what-is-the-purpose-of-returnsobjectsasfaults","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/ali-akhtar.medium.com\/mastering-in-coredata-part-10-nsfetchrequest-a011684dd8f7"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/stackoverflow.com\/questions\/10532626\/how-to-fetch-just-object-ids-but-also-include-the-row-data-in-coredata\/23007159#23007159","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/stackoverflow.com\/questions\/36541764\/how-to-rearrange-item-of-an-array-to-new-position-in-swift\/43273824","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"schemaVersion":{"minor":3,"major":0,"patch":0},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220210_Core_Graphics,_codegen,_returnsObjectsAsFaults,_NSFetchRequestResult,_Array_item_to_new_position","interfaceLanguage":"swift"},"kind":"article","references":{"https://developer.apple.com/documentation/coregraphics/cgcontext/1455270-setlinewidth":{"titleInlineContent":[{"text":"setLineWidth(_:)","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontext\/1455270-setlinewidth","title":"setLineWidth(_:)","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontext\/1455270-setlinewidth"},"https://i.imgur.com/R2S3Ydm.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/R2S3Ydm.png","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/i.imgur.com\/R2S3Ydm.png"},"https://i.imgur.com/5xX9tAK.gif":{"alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/5xX9tAK.gif"}],"type":"image","identifier":"https:\/\/i.imgur.com\/5xX9tAK.gif"},"https://developer.apple.com/documentation/coregraphics/cgcontext/1456420-addellipse":{"titleInlineContent":[{"type":"text","text":"addEllipse(in:)"}],"url":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontext\/1456420-addellipse","title":"addEllipse(in:)","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontext\/1456420-addellipse"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://developer.apple.com/documentation/coregraphics/cgcontext/1454079-setfillcolor":{"titleInlineContent":[{"text":"setFillColor(_:)","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontext\/1454079-setfillcolor","title":"setFillColor(_:)","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontext\/1454079-setfillcolor"},"https://i.imgur.com/KvpeEAa.gif":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/KvpeEAa.gif","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/i.imgur.com\/KvpeEAa.gif"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://developer.apple.com/documentation/coregraphics/cgcontext/1456635-beginpath":{"titleInlineContent":[{"text":"beginPath","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontext\/1456635-beginpath","title":"beginPath","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontext\/1456635-beginpath"},"https://i.imgur.com/ZbWKJo1.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/ZbWKJo1.png","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/i.imgur.com\/ZbWKJo1.png"},"https://developer.apple.com/documentation/coregraphics/cgrect/1454218-insetby":{"titleInlineContent":[{"text":"insetBy(dx:dy:)","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgrect\/1454218-insetby","title":"insetBy(dx:dy:)","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgrect\/1454218-insetby"},"https://i.imgur.com/Be2uB1E.gif":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/Be2uB1E.gif","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/i.imgur.com\/Be2uB1E.gif"},"https://developer.apple.com/documentation/uikit/uitableview/1614987-moverow":{"titleInlineContent":[{"text":"UITableView - moveRow(at:to:)","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableview\/1614987-moverow","title":"UITableView - moveRow(at:to:)","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableview\/1614987-moverow"},"https://i.imgur.com/33HbCPg.jpg":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/33HbCPg.jpg","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/i.imgur.com\/33HbCPg.jpg"},"https://developer.apple.com/documentation/coregraphics/cgcontextref":{"titleInlineContent":[{"text":"CGContextRef","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontextref","title":"CGContextRef","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/coregraphics\/cgcontextref"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220211_uicontextualaction,_uialertaction,_uiactivityviewcontroller,_setselected,_selectedbackgroundview,_actionsheet,_popoverpresentationcontroller,_viewwilltransition.json b/docs/data/documentation/arinote/220211_uicontextualaction,_uialertaction,_uiactivityviewcontroller,_setselected,_selectedbackgroundview,_actionsheet,_popoverpresentationcontroller,_viewwilltransition.json new file mode 100644 index 0000000..2514e0b --- /dev/null +++ b/docs/data/documentation/arinote/220211_uicontextualaction,_uialertaction,_uiactivityviewcontroller,_setselected,_selectedbackgroundview,_actionsheet,_popoverpresentationcontroller,_viewwilltransition.json @@ -0,0 +1 @@ +{"kind":"article","variants":[{"paths":["\/documentation\/arinote\/220211_uicontextualaction,_uialertaction,_uiactivityviewcontroller,_setselected,_selectedbackgroundview,_actionsheet,_popoverpresentationcontroller,_viewwilltransition"],"traits":[{"interfaceLanguage":"swift"}]}],"schemaVersion":{"patch":0,"minor":3,"major":0},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220211_UIContextualAction,_UIAlertAction,_UIActivityViewController,_setSelected,_selectedBackgroundView,_actionSheet,_popoverPresentationController,_viewWillTransition","interfaceLanguage":"swift"},"primaryContentSections":[{"content":[{"level":1,"text":"TIL (Today I Learned)","type":"heading","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"text":"2월 11일 (금)","type":"text"}]},{"level":2,"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"UIContextualAction","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"UIAlertAction"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIActivityViewController"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"UITableViewCell - "},{"code":"setSelected","type":"codeVoice"},{"type":"text","text":", "},{"type":"codeVoice","code":"selectedBackgroundView"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"iPad에서 ActionSheet를 Present를 하면 크래쉬가 발생"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"popoverPresentationController","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"viewWillTransition"}],"type":"paragraph"}]}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","type":"heading","level":2},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[UIContextualAction에 텍스트말고 아이콘 삽입하는 방법]","type":"text"}]}]},{"code":["let deleteAction = UIContextualAction(style: .destructive, title: nil) { _, _, completeHandeler in"," self.deleteCell(indexPath: indexPath)"," completeHandeler(true)","}","deleteAction.image = UIImage(systemName: \"trash.fill\")"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"inlineContent":[{"text":"먼저 ","type":"text"},{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uicontextualaction","type":"reference","isActive":true},{"type":"text","text":" 인스턴스를 생성한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"생성할 때 title이 nil인게 포인트이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이후 생성한 UIContextualAction에 image를 대입해주면 된다."}]}]}],"type":"unorderedList"},{"style":"note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"적용된 모습"}]}],"name":"Note"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/SdZTntY.png","type":"image"}],"type":"paragraph"},{"style":"note","content":[{"inlineContent":[{"type":"codeVoice","code":"의문점"},{"text":" ","type":"text"},{"text":"UIContextualAction 파라미터 중 handler의 용도는 무엇일까?","type":"text"},{"text":" ","type":"text"},{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uicontextualaction\/handler","type":"text"}],"type":"paragraph"}],"name":"Note","type":"aside"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"handler의 작업이 실제로 수행된 경우 핸들러에 true를 전달하여 작업이 완료되었다는 것을 알려주는 용도라고 한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"지금같이 간단한 로직인 경우 그냥 true로 기입해주면 되겠지만, 만약 복잡한 로직이 추가되어 에러처리를 해줘야하는 경우에는 false를 전달하여 작업이 완료되지 않았다는 것을 알리는 용도인 것 같다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/forums\/thread\/129420"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"여기 글을 참고하니 현재는 completeHandeler를 사용하지 않지만, 나중에 사용할 수 있으므로 적절한 값을 전달하는 것을 권장한다는 답변이 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그래서 팀원들과 의논하여 true로 기입해주기로 하였다.","type":"text"}]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[UIAlertAction 편집하기]"}]}],"type":"paragraph"},{"content":[{"inlineContent":[{"type":"text","text":"단순한 글자말고 여기에도 아이콘같은걸 추가할 수 있을까?"}],"type":"paragraph"}],"style":"note","name":"Note","type":"aside"},{"code":["let deleteAction = UIAlertAction(title: \"Delete\", style: .destructive, handler: deleteHandler)","let deleteImage = UIImage(systemName: \"trash.fill\")","deleteAction.setValue(deleteImage, forKey: \"image\")","deleteAction.setValue(0, forKey: \"titleTextAlignment\")"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"setValue 메소드를 통해서 지정해줄 수 있었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"단순하게 이미지와 텍스트의 alignment를 지정해주었다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"0 - left"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"1 - center","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"2 - right"}]}]}]}]}],"type":"unorderedList"},{"content":[{"type":"paragraph","inlineContent":[{"text":"적용된 모습","type":"text"}]}],"type":"aside","style":"note","name":"Note"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/ke4Oujp.png"}],"type":"paragraph"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[Cell이 select 되었을 때 background 색깔 입혀주기]","type":"text"}]}],"type":"paragraph"},{"name":"Note","type":"aside","style":"note","content":[{"type":"paragraph","inlineContent":[{"text":"메모를 선택하고 수정하고 있을 때 선택한 Cell의 배경색을 입혀주어 선택한 셀을 수정하고 있다는 느낌을 주고 싶었다.","type":"text"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"먼저 커스텀한 UITableViewCell 클래스에서 ","type":"text"},{"isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewcell\/1623255-setselected","type":"reference"},{"type":"text","text":" 메소드를 오버라이드 해주자."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그리고 selected 여부에 따라 뷰의 변화를 주는 로직을 추가한다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"syntax":"swift","code":["override func setSelected(_ selected: Bool, animated: Bool) {"," super.setSelected(selected, animated: animated)"," if selected {"," titleLabel.textColor = .white"," dateLabel.textColor = .systemGray6"," bodyLabel.textColor = .systemGray6"," } else {"," titleLabel.textColor = .label"," dateLabel.textColor = .systemGray"," bodyLabel.textColor = .systemGray"," }","}"],"type":"codeListing"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"그리고 Cell이 초기화 될 때 "},{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewcell\/1623226-selectedbackgroundview","type":"reference","isActive":true},{"type":"text","text":"도 할당해준다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"syntax":"swift","type":"codeListing","code":["private func setUpViews() {"," ..."," let selectedBackgroundView = UIView()"," selectedBackgroundView.backgroundColor = .systemOrange"," self.selectedBackgroundView = selectedBackgroundView","}"]},{"type":"aside","content":[{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"메모","type":"text"}],"type":"strong"},{"type":"text","text":" "},{"text":"setSelected를 오버라이드 하지 않고 단순히 selectedBackgroundView만 할당해준다면, 셀을 선택해도 배경색이 바뀌지 않는다.","type":"text"}]}],"style":"note","name":"Note"},{"type":"unorderedList","items":[{"content":[{"text":"완성된 모습","anchor":"%EC%99%84%EC%84%B1%EB%90%9C-%EB%AA%A8%EC%8A%B5","type":"heading","level":3}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/pli9vhF.gif"}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[iPad에서 UIAlertController의 actionSheet 사용시 발생하는 오류]","type":"text"}],"type":"strong"}]},{"style":"note","type":"aside","name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"오류메세지","type":"text"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"UIActivityViewController를 present를 해주려는데 아래와 같은 오류메세지가 떴다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"codeListing","syntax":null,"code":["Thread 1: \"Your application has presented a UIAlertController () of style UIAlertControllerStyleActionSheet from CloudNotes.SplitViewController ().","The modalPresentationStyle of a UIAlertController with this style is UIModalPresentationPopover. ","You must provide location information for this popover through the alert controller's popoverPresentationController.","You must provide either a sourceView and sourceRect or a barButtonItem. ","If this information is not known when you present the alert controller, you may provide it in the UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.\""]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"간단히 해석하자면 iPad에서 액션시트를 present를 할 경우 모달스타일이 UIModalPresentationPopover이고, 이걸 사용할 때는 barButtonItem 또는 해당 창의 대한 위치를 설정해주어야 한다고 되어있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 설정해주어야 하는 것은 2가지중 하나이다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"필수적으로 sourceView 지정해주기"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621428-popoverpresentationcontroller","isActive":true},{"type":"text","text":"에 sourceRect 또는 barButtonItem 할당해주기"}]}]}]}]}],"type":"unorderedList"},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해결 방법"}]}],"type":"aside","style":"note","name":"Note"},{"type":"paragraph","inlineContent":[{"type":"text","text":"얼럿을 present 해주기 전에 다음과 같은 if문을 추가해주자!"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"UIBarButtonItem에 추가해주는 방법","type":"text"}]}]}],"type":"unorderedList"},{"code":["\/\/ UIViewController extension 내부...","if let popoverController = activityViewController.popoverPresentationController {"," popoverController.sourceView = self.splitViewController?.view"," popoverController.barButtonItem = sender \/\/ 메소드 내부라서 파라미터로 barButtonItem 전달받아 할당해주었다.","}"],"type":"codeListing","syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"위치를 정해주는 방법","type":"text"}]}]}]},{"code":["if let popoverController = activityViewController.popoverPresentationController {"," popoverController.sourceView = self.splitViewController?.view \/\/ present할 뷰 지정"," popoverController.sourceRect = CGRect( \/\/ 뷰의 정 가운데 위치로 지정"," x: self.splitViewController?.view.bounds.midX,"," y: self.splitViewController?.view.bounds.midY,"," width: 0,"," height: 0"," )"," popoverController.permittedArrowDirections = [] \/\/ 화살표를 빈배열로 대입","}"],"syntax":"swift","type":"codeListing"},{"name":"Note","content":[{"level":3,"anchor":"%EC%9D%98%EB%AC%B8%EC%A0%90","text":"의문점","type":"heading"},{"type":"paragraph","inlineContent":[{"text":"위치를 지정하고 나서 화면을 돌렸는데… 가운데 위치에 안있고 요상한 곳에 있다….","type":"text"},{"text":" ","type":"text"},{"type":"image","identifier":"https:\/\/i.imgur.com\/9Dk3gSK.gif"}]}],"type":"aside","style":"note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"해결방법","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"화면이 돌아갈 때마다 포지션을 다시 잡아주면 된다. 그걸 위해 "},{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uicontentcontainer\/1621466-viewwilltransition","type":"reference","isActive":true},{"type":"text","text":" 메소드를 활용해보겠다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"이 메소드는 ViewController의 뷰 크기를 변경하기 전에 호출이 된다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"일단 얼럿을 present하는 뷰에 popoverController라는 변수를 만들어준다.","type":"text"}]}]}]},{"code":["class SplitViewController: UISplitViewController {"," ..."," var popoverController: UIPopoverPresentationController?"],"type":"codeListing","syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 viewWillTransition 메소드를 오버라이드하여 위치를 고쳐주는 로직을 추가한다."}]}]}]},{"type":"codeListing","syntax":"swift","code":["if let popoverController = self.popoverController {"," popoverController.sourceRect = CGRect("," x: size.width * 0.5,"," y: size.height * 0.5,"," width: 0,"," height: 0)","}"]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"UIViewController extension으로 만들어준 메소드 내부(맨 처음 얼럿을 생성하여 present하는 곳)에도 popoverController를 할당해주도록 해주었다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["let splitViewController = self.splitViewController as? SplitViewController","splitViewController?.popoverController = popoverController"]},{"type":"aside","content":[{"type":"heading","anchor":"%ED%95%B4%EA%B2%B0%EB%90%9C-%EB%AA%A8%EC%8A%B5","level":3,"text":"해결된 모습"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/uy2XqZj.gif"}]}],"name":"Note","style":"note"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/stackoverflow.com\/questions\/30056236\/uialertaction-list-of-values"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/royhelen.tistory.com\/53"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/devsc.tistory.com\/76","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@dlskawns96\/UITableViewCell%EC%97%90-%EC%BB%A4%EC%8A%A4%ED%85%80-Swipe-action-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0-Swift"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/nemecek.be\/blog\/5\/how-to-implement-swipe-to-delete-action-with-custom-icon"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/stackoverflow.com\/questions\/26347085\/add-image-to-uialertaction-in-uialertcontroller"}]}]}]}]}]}],"kind":"content"}],"metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","title":"220211 UIContextualAction, UIAlertAction, UIActivityViewController, setSelected, selectedBackgroundView, actionSheet, popoverPresentationController, viewWillTransition"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"sections":[],"references":{"https://developer.apple.com/documentation/uikit/uiviewcontroller/1621428-popoverpresentationcontroller":{"titleInlineContent":[{"type":"text","text":"popoverPresentationController"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621428-popoverpresentationcontroller","title":"popoverPresentationController","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uiviewcontroller\/1621428-popoverpresentationcontroller"},"https://i.imgur.com/pli9vhF.gif":{"identifier":"https:\/\/i.imgur.com\/pli9vhF.gif","variants":[{"url":"https:\/\/i.imgur.com\/pli9vhF.gif","traits":["1x","light"]}],"type":"image","alt":null},"https://i.imgur.com/SdZTntY.png":{"identifier":"https:\/\/i.imgur.com\/SdZTntY.png","variants":[{"url":"https:\/\/i.imgur.com\/SdZTntY.png","traits":["1x","light"]}],"type":"image","alt":null},"https://i.imgur.com/9Dk3gSK.gif":{"identifier":"https:\/\/i.imgur.com\/9Dk3gSK.gif","variants":[{"url":"https:\/\/i.imgur.com\/9Dk3gSK.gif","traits":["1x","light"]}],"type":"image","alt":null},"https://i.imgur.com/uy2XqZj.gif":{"identifier":"https:\/\/i.imgur.com\/uy2XqZj.gif","variants":[{"url":"https:\/\/i.imgur.com\/uy2XqZj.gif","traits":["1x","light"]}],"type":"image","alt":null},"https://developer.apple.com/documentation/uikit/uicontextualaction":{"titleInlineContent":[{"text":"UIContextualAction","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uicontextualaction","title":"UIContextualAction","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uicontextualaction"},"https://developer.apple.com/documentation/uikit/uitableviewcell/1623255-setselected":{"titleInlineContent":[{"type":"text","text":"setSelected(_:animated:)"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewcell\/1623255-setselected","title":"setSelected(_:animated:)","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewcell\/1623255-setselected"},"https://developer.apple.com/documentation/uikit/uitableviewcell/1623226-selectedbackgroundview":{"titleInlineContent":[{"text":"selectedBackgroundView","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewcell\/1623226-selectedbackgroundview","title":"selectedBackgroundView","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewcell\/1623226-selectedbackgroundview"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/ke4Oujp.png":{"type":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/ke4Oujp.png"}],"alt":null,"identifier":"https:\/\/i.imgur.com\/ke4Oujp.png"},"https://developer.apple.com/documentation/uikit/uicontentcontainer/1621466-viewwilltransition":{"titleInlineContent":[{"text":"viewWillTransition","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uicontentcontainer\/1621466-viewwilltransition","title":"viewWillTransition","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uicontentcontainer\/1621466-viewwilltransition"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220214_core_animation,_cabasicanimation,_append,_escaping,_uitableview-crash,_uitextviewdelegate,_typingattributes.json b/docs/data/documentation/arinote/220214_core_animation,_cabasicanimation,_append,_escaping,_uitableview-crash,_uitextviewdelegate,_typingattributes.json new file mode 100644 index 0000000..1c28a52 --- /dev/null +++ b/docs/data/documentation/arinote/220214_core_animation,_cabasicanimation,_append,_escaping,_uitableview-crash,_uitextviewdelegate,_typingattributes.json @@ -0,0 +1 @@ +{"kind":"article","metadata":{"title":"220214 Core Animation, CABasicAnimation, append, escaping, UITableView-Crash, UITextViewDelegate, typingAttributes","roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"role":"article"},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220214_Core_Animation,_CABasicAnimation,_append,_escaping,_UITableView-Crash,_UITextViewDelegate,_typingAttributes"},"sections":[],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"primaryContentSections":[{"kind":"content","content":[{"type":"heading","level":1,"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)"},{"inlineContent":[{"text":"2월 14일 (월)","type":"text"}],"type":"paragraph"},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading","text":"학습 내용","level":2},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"월요일 활동학습","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"오답노트"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Core Animation","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UITableView Crash 문제해결"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Thread 1: Attempted to scroll the table view to an out-of-bounds row (0) when there are only 0 rows in section 0."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Thread 1: Invalid update: invalid number of rows in section 0"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UITextView - 실시간으로 폰트 변경하는 방법 구현하기"}]}]}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"type":"heading","text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[활동학습 퀴즈 오답노트]"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Array타입의 append 메서드의 시간복잡도는 항상 O(1)이다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"아니다. 일반적으로는 O(1)이지만 메모리 공간을 재할당해야하는 경우에는 시간복잡도가 O(n)이다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"SOLID 원칙 복기하기","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"SRP (Single responsibility principle)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"단일 책임 원칙"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"한 클래스는 하나의 책임만 가져야 한다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"OCP (Open\/closed principle)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"개방-폐쇄 원칙","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"소프트웨어의 요소는 확장에는 열려있으나 변경에는 닫혀 있어야 한다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"LSP (Liskov substitution principle)"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"리스코프 치환 원칙","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"ISP (Interface segregation principle"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"인터페이스 분리 원칙"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"DIP (Dependency inversion principle)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"의존관계 역전 원칙","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"프로그래머는 “추상화에 의존해야지, 구체화에 의존하면 안된다”","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"의존성 주입은 이 원칙을 따르는 방법 중 하나다.","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"탈출 클로저를 만들 때 매개변수의 타입은?"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"@escaping () -> Void","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"애만 되는 줄 알았는데"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"(() -> Void)?"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이렇게 옵셔널 형태가 되면 자동으로 escaping 클로저가 된다."}]}]}]}]}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[Relationship between a view’s frame and bounds]","type":"text"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/library\/archive\/documentation\/WindowsViews\/Conceptual\/ViewPG_iPhoneOS\/WindowsandViews\/WindowsandViews.html"}]},{"type":"paragraph","inlineContent":[{"text":"frame, bounds및 center properties은 다른 property과 별도로 변경할 수 있지만, 한 properties을 변경하면 아래와 같은 방식으로 다른 properties에 영향을 준다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"frame property를 설정하면 bounds property의 size 값이 frame rectangle의 new size와 일치되도록 변경된다. center property의 값은 frame rectangle의 new center point과 일치하도록 유사하게 변경된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"center property를 설정하면 frame의 원점 값이 그에 따라 변경된다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"bounds property의 size를 설정하면 frame property의 size 값이 bounds rectangle의 new size와 일치하도록 변경된다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"aside","style":"note","content":[{"inlineContent":[{"type":"text","text":"기본적으로 view의 frame은 superView의 frame에 잘리지 않는다. "},{"type":"text","text":" "},{"type":"text","text":"따라서 superview의 frame 외부에 있는 모든 subviews는 전체적으로 렌더링 된다. "},{"type":"text","text":" "},{"type":"text","text":"superview의 clipsToBounds property를 true로 설정하면 이 동작을 변경할 수 있다. "},{"text":" ","type":"text"},{"text":"subviews가 시각적으로 잘려있는지 여부에 관계없이 touch events는 항상 target view의 bounds rectangle을 따른다. ","type":"text"},{"type":"text","text":" "},{"type":"text","text":"즉, superview의 bounds rectangle 외부에 있는 view의 일부에서 발생하는 touch events는 해당 view에 전달되지 않는다. "}],"type":"paragraph"}],"name":"Note"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Core Animation]"}]}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/library\/archive\/documentation\/Cocoa\/Conceptual\/CoreAnimation_guide\/CoreAnimationBasics\/CoreAnimationBasics.html#\/\/apple_ref\/doc\/uid\/TP40004514-CH2-SW3"}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/11lMLCn.png"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"CAAnimation","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"대부분의 애니메이션들을 관리하는 추상 클래스","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이게 코어 애니메이션의 전부는 아니지만 주로, 그리고 처음부터 많이 쓰게 될 클래스이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"상속받은 추상 클래스","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"CAAnimationGroup"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"CAPropertyAnimation (추상클래스)"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"CABasicAnimation","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"CAKeyframeAnimation"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"CATransition"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"inlineContent":[{"inlineContent":[{"text":"[Core Animation이란?]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"시각적 요소에 대해 그래픽 렌더링 및 구성을 통해 애니메이션을 만드는 프레임워크","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"시작 및 끝 포인트의 매개변수를 구성하고 애니메이션을 구현하면 Task가 자동으로 일을 수행한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"드로잉 작업을 그래픽 하드웨어로 전달하여 레이어 객체가 조작할 수 있도록 렌더링 작업을 가속화해 앱 속도 및 품질의 다운없이 높은 프레임과 자연스러운 애니메이션을 보여준다.","type":"text"}],"type":"paragraph"}]},{"content":[]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/axMWwD0.png","type":"image"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"때로는 UiKit에서 원하는 유형의 애니메이션을 수행할 수 없을 수도 있으며, 원하는 효과를 얻을 수 있는 것은 Core Animation으로 직접 드롭다운해야 한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UIKit에서 작업하든 SwiftUI에서 작업하든 관계없이 모든 애니메이션은 결국 이 프레임워크를 통해 렌더링 된다. 작동 방식을 이해한다면 이러한 상위 수준 API로 작업하기 더 쉬워진다.","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/pDAfuLt.png","type":"image"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"코어 애니메이션은 레이어에서 작동한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뷰의 속성을 애니메이션 및 변경하는데 사용된다. UIView에는 기본 CALayer가 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기본적으로 시작 및 중지 위치를 지정한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"레이어에 애니메이션을 추가하면 Core Animation이 나머지 두 상태 사이의 이미지를 보간하고 화면에서 애니메이션을 처리한다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[CALayer]","type":"text"}]}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"컨텐츠를 관리\/조작하는데 사용된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"그래픽 하드웨어로 쉽게 조작할 수 있도록 비트맵으로 컨텐츠를 캡쳐한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"뷰와의 차이는 자체 형태를 정의하지 않는다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"비트맵으로 구성된 상태정보만 관리","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"레이어가 앱에서 실제 드로잉 작업을 하진 않는다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"변경사항을 가진 애니메이션 트리거시 레이어 비트맵\/상태정보 -> 그래픽 하드웨어 전달"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"UIView에 최소 1개씩 있고 렌더링을 담당한다. (자신을 그려줄 레이어)"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"3D에 구성된 2D (레이어 객체)"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"레이어는 두가지 유형의 좌표계를 사용하여 애니메이션을 만든다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"점 기반 좌표계","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"단위 기반 좌표계"}]}]}]}]}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/bGJxB99.png"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[Layer Tree의 종류]","type":"text"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"코어 애니메이션은 CALayer의 프로퍼티를 직접 건드리지 않고 아래와 같은 3개의 Layer Tree를 가지고 관리"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Model","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"해당 레이어의 실제 상태 값"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Presentation"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"애니메이션 중에만 관리하며 뷰에 표시되는 현재 상태값을 반영"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Render"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"실제 애니메이션을 수행하는 코어 애니메이션 전용"}]}]}],"type":"unorderedList"}]}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/yFYMC1X.png","type":"image"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Core Animation의 다양한 프로퍼티"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/library\/archive\/documentation\/Cocoa\/Conceptual\/CoreAnimation_guide\/AnimatableProperties\/AnimatableProperties.html#\/\/apple_ref\/doc\/uid\/TP40004514-CH11-SW1"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Core Animation 만들어보기 - 직선으로 움직이는 뷰]"}]}]},{"type":"codeListing","syntax":"swift","code":["class ViewController: UIViewController {",""," let redView: UIView = {"," let view = UIView(frame: CGRect(x: 20, y: 100, width: 140, height: 100))"," view.backgroundColor = .systemRed"," return view"," }()"," "," let button: UIButton = {"," let button = UIButton()"," button.translatesAutoresizingMaskIntoConstraints = false"," button.setTitle(\"Animate\", for: .normal)"," button.titleLabel?.adjustsFontSizeToFitWidth = true"," button.backgroundColor = .systemBlue"," button.layer.cornerRadius = 8"," return button"," }()"," "," override func viewDidLoad() {"," super.viewDidLoad()"," "," view.addSubview(redView)"," view.addSubview(button)"," button.addTarget(self, action: #selector(buttonTapped(_:)), for: .primaryActionTriggered)"," "," NSLayoutConstraint.activate(["," view.safeAreaLayoutGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: button.bottomAnchor, multiplier: 2),"," button.centerXAnchor.constraint(equalTo: view.centerXAnchor),"," ])"," }"," "," func animate() {"," let animation = CABasicAnimation()"," animation.keyPath = \"position.x\" \/\/ keyPath를 사용하여 어떤 애니메이션을 만들고 싶은지 알린다."," \/\/ 애니메이션의 시작 및 종료상태를 정의한다."," animation.fromValue = 20 + 140 \/ 2"," animation.toValue = 300"," animation.duration = 1"," \/*"," 이해하기 까다로운 점은 shapes position 또는"," anchor point의 코어 애니메이션 작업이다."," position은 도형 중간이다."," 따라서 rectangle을 왼쪽에서 오른쪽으로 이동하려면"," 먼저 rectangle의 가운데 x점 값을 계산한 다음"," 직사각형의 가운데 마지막 x 위치를 계산하여"," fromValue와 toValue로 만들어야 한다."," *\/",""," redView.layer.add(animation, forKey: \"basic\") \/\/ 뷰의 레이어에 애니메이션을 추가"," redView.layer.position = CGPoint(x: 300, y: 100 + 100\/2) \/\/ 최종 애니메이션의 위치를 업데이트",""," \/*"," 이 마지막 단계는 매우 중요합니다."," 앞서 언급한 바와 같이 Core Animation는 모델 레이어(현재 상태)과 프레젠테이션 레이어(애니메이션 상태)의 두 가지 레이어을 유지합니다."," 그리고 애니메이션이 끝나면 final presentation state를 반영하여 model state를 업데이트해야 합니다."," *\/"," }","}"]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[UITableView Cell을 selectRow를 호출했을 때 발생한 Crash]"}]}],"type":"paragraph"},{"type":"aside","style":"note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UITableView의 selectRow를 통해 Select를 시도했을 때, 아래와 같은 에러가 나면서 Crash가 발생했다."}]}],"name":"Note"},{"code":["Thread 1: ","\"Attempted to scroll the table view to an out-of-bounds row (0) when there are only 0 rows in section 0. ","Table view: ;","layer = ; contentOffset: {0, -74}; ","contentSize: {420, 72.5}; adjustedContentInset: {74, 0, 20, 0}; ","dataSource: >\""],"syntax":null,"type":"codeListing"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"상황"},{"type":"text","text":" 메모장의 마지막 남은 셀을 지우게 되면서 selectRow가 호출이 되는 상황이였다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"이유","type":"codeVoice"},{"type":"text","text":" "},{"type":"strong","inlineContent":[{"type":"text","text":"셀을 지우고 난 후"}]},{"type":"text","text":"니까 UITableView에 보여줄 데이터가 존재하지 않고, Cell도 존재하지 않는 상황이였는데, "},{"code":"존재하지 않는 셀","type":"codeVoice"},{"type":"text","text":"을 "},{"type":"codeVoice","code":"Select"},{"type":"text","text":"를 하려고 해서 크래쉬가 난 것이였다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"해결"},{"text":" 따라서 Select를 하기 전에 먼저 UITableView에 ","type":"text"},{"type":"codeVoice","code":"numberOfRows(inSection:)"},{"text":" 메소드를 통해 해당 값이 0이 아닐 경우에만 seletRow를 호출할 수 있도록 guard문을 추가하여 해결해주었다.","type":"text"}]}]}],"type":"unorderedList"},{"syntax":"swift","code":["func updateData(at index: Int) {"," guard self.tableView.numberOfRows(inSection: .zero) != .zero else {"," return"," }"," ..."," tableView.selectRow(at: IndexPath(row: .zero, section: .zero), animated: false, scrollPosition: .middle)","}"],"type":"codeListing"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[UITableView의 Cell을 deleteRows로 지웠을 때 발생한 Crash]","type":"text"}],"type":"strong"}]},{"name":"Note","content":[{"inlineContent":[{"type":"text","text":"메모장의 데이터를 JSON으로 파싱한 샘플 모델에서 Core Data로 리팩토링하는 과정에서 난 에러였다."}],"type":"paragraph"}],"type":"aside","style":"note"},{"code":["Thread 1: ","\"Invalid update: invalid number of rows in section 0. ","The number of rows contained in an existing section after the update (15) must be equal to the number of rows contained in that section before the update (15), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out)."," Table view: ","; layer = ; ","contentOffset: {0, -74}; ","contentSize: {420, 1160}; ","adjustedContentInset: {74, 0, 20, 0};"," dataSource: >\""],"type":"codeListing","syntax":null},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"상황"},{"type":"text","text":" 테이블 뷰의 "},{"code":"섹션의 행 개수","type":"codeVoice"},{"text":"와 ","type":"text"},{"type":"codeVoice","code":"실제 보여줄 섹션 개수"},{"text":"가 맞지 않아서 발생하는 오류이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"이유","type":"codeVoice"},{"text":" 테이블뷰의 셀을 삭제하면서 테이블뷰에 보여줄 데이터도 동일하게 삭제처리를 해주어야 하는데, 누락되서 발생한 것이였다.","type":"text"}]}]},{"content":[{"inlineContent":[{"code":"해결","type":"codeVoice"},{"type":"text","text":" 셀을 "},{"type":"codeVoice","code":"추가"},{"text":", ","type":"text"},{"type":"codeVoice","code":"삭제"},{"text":"할 때 테이블뷰에 보여줄 섹션의 개수도 동일할 수 있도록 PersistentManager의 notes 관리(배열 요소 제거, 코어데이터 요소 제거)도 빼먹지 않도록 해주었다.","type":"text"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[UITextView - 실시간으로 폰트 변경하는 방법 구현하기]"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"메모장 앱 구현 시 맨 첫줄에는 큰 제목이, 그 아래부터는 본문 글꼴로 설정하여 제목과 내용을 구분해줄 수 있는 방법은 무엇인지 찾아보았다."}],"type":"paragraph"}],"style":"note","name":"Note","type":"aside"},{"content":[{"inlineContent":[{"text":"UITextViewDelegate - ","type":"text"},{"type":"reference","isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitextviewdelegate\/1618630-textview"},{"text":" 메소드를 활용하여 해결해보았다.","type":"text"}],"type":"paragraph"}],"name":"Note","type":"aside","style":"note"},{"syntax":"swift","code":["func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {"," let currentText = textView.text as NSString \/\/ 타이핑 후 텍스트"," let titleRange = currentText.range(of: Constant.lineBreak.description) \/\/ title의 range를 linebreak를 활용하여 구한다."," if titleRange.location < range.location { \/\/ title의 location과 전체 텍스트의 location을 비교"," textView.typingAttributes = Constant.bodyAttributes \/\/ title보다 전체 텍스트의 location이 크다면"," } else {"," textView.typingAttributes = Constant.headerAttributes \/\/ title의 location이 전체 텍스트보다 크다면"," }"," return true","}"],"type":"codeListing"},{"inlineContent":[{"type":"text","text":"\b 텍스트를 입력할 때마다 title의 location과 전체 텍스트의 location을 비교하여 typingAttributes를 할당해주도록 분기처리하였다."}],"type":"paragraph"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/library\/archive\/documentation\/Cocoa\/Conceptual\/CoreAnimation_guide\/CoreAnimationBasics\/CoreAnimationBasics.html#\/\/apple_ref\/doc\/uid\/TP40004514-CH2-SW3"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/www.youtube.com\/watch?v=93bfh3GK79s","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/hartl.co\/2015\/05\/21\/Highlight-first-line.html"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/green1229.tistory.com\/76","type":"text"}]}]}]}]}]}]}],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220214_core_animation,_cabasicanimation,_append,_escaping,_uitableview-crash,_uitextviewdelegate,_typingattributes"]}],"schemaVersion":{"major":0,"patch":0,"minor":3},"references":{"https://i.imgur.com/yFYMC1X.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/yFYMC1X.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/yFYMC1X.png","traits":["1x","light"]}]},"https://developer.apple.com/documentation/uikit/uitextviewdelegate/1618630-textview":{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitextviewdelegate\/1618630-textview","title":"textView(_:shouldChangeTextIn:replacementText:)","titleInlineContent":[{"text":"textView(_:shouldChangeTextIn:replacementText:)","type":"text"}],"type":"link","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uitextviewdelegate\/1618630-textview"},"https://i.imgur.com/axMWwD0.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/axMWwD0.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/axMWwD0.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/pDAfuLt.png":{"alt":null,"identifier":"https:\/\/i.imgur.com\/pDAfuLt.png","type":"image","variants":[{"url":"https:\/\/i.imgur.com\/pDAfuLt.png","traits":["1x","light"]}]},"https://i.imgur.com/bGJxB99.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/bGJxB99.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/bGJxB99.png","traits":["1x","light"]}]},"https://i.imgur.com/11lMLCn.png":{"alt":null,"identifier":"https:\/\/i.imgur.com\/11lMLCn.png","type":"image","variants":[{"url":"https:\/\/i.imgur.com\/11lMLCn.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220215_dropbox,_weak_self,_escaping,_delayed_deallocation,_performbatchupdate,_setediting.json b/docs/data/documentation/arinote/220215_dropbox,_weak_self,_escaping,_delayed_deallocation,_performbatchupdate,_setediting.json new file mode 100644 index 0000000..86105b7 --- /dev/null +++ b/docs/data/documentation/arinote/220215_dropbox,_weak_self,_escaping,_delayed_deallocation,_performbatchupdate,_setediting.json @@ -0,0 +1 @@ +{"sections":[],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220215_Dropbox,_weak_self,_escaping,_Delayed_Deallocation,_performBatchUpdate,_setEditing"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"primaryContentSections":[{"content":[{"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","level":1,"type":"heading"},{"inlineContent":[{"type":"text","text":"2월 15일 (화)"}],"type":"paragraph"},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용","type":"heading","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Dropbox는 뭘까…"}]}]},{"content":[{"inlineContent":[{"text":"weak self는 언제 사용할까?","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UITableView - performBatchUpdate","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UITableView - 셀을 스와이프 했을 때 select 유지하기"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","level":2},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Dropbox]"}]}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"아이클라우드, 구글 드라이브 같은 놈이다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"https:\/\/www.dropbox.com\/developers\/apps","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"먼저 위 링크로 들어가서 가입하고 APP_KEY를 생성해야 한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그리고 info.plist에 다음과 같은 key를 추가해야 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/dropbox\/SwiftyDropbox\/blob\/master\/Images\/InfoPlistExample.png?raw=true","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"DropboxClientsManager를 활용해서 인증, 로그인, 다운로드, 업로드 등등을 할 수 있는 것 같은데, 더 자세히 보려면 일단 프로젝트에 라이브러리 설정부터 해야할 것 같다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"튜토리얼만 쭉 훑어보았다…"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[weak self는 언제 사용해야할까?]"}]}]},{"name":"Note","content":[{"inlineContent":[{"text":"코드리뷰 받던 중 escaping 관련 질문이 들어왔는데.. 훑어본 기억만 있지 자세히 알아보진 않았던 것 같아서 찾아보았다.","type":"text"}],"type":"paragraph"}],"type":"aside","style":"note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"escaping은 주로 네트워킹을 할 때 사용하게 되는데, 그 이유는 네트워킹의 경우 실행이 언제 종료되는지 예측하기 어렵기 때문이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"non-escaping은 코드를 즉시 실행하고 나중에 저장하거나 실행할 수 없다. scope 내에서 실행되기 때문이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"escaping은 저장될 수 있고 다른 클로저로 전달될 수 있으며 미래의 어느 시점에 실행될 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"escaping 과 non-escaping의 가장 큰 차이는 메모리적인 차이가 존재한다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"escaping을 붙이게 되면 클로저 내부를 실행하기 위해 캡쳐를 한다."}]}]},{"content":[{"inlineContent":[{"text":"즉 메모리를 확보한다는 말이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"만약 클로저 내부에 엄청 느린 로직이 있어서 객체가 죽는 시점보다 클로저 종료 시점이 더 느린 상황이 온다면, 객체가 죽어야하는데… 클로저 실행이 끝나기 전까지는 self가 죽지 않고 살아서, 메모리가 해제되지 않는다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉 메모리 누수가 발생한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 weak를 사용하게 되는데, weak를 붙이게 되면 참조하고 있는 객체가 죽어버리면, 아예 클로저 자체를 실행하지 않고 날려버린다.","type":"text"}]}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그렇다면 weak self는 언제 사용하게 될까?"}],"type":"paragraph"}],"style":"note","type":"aside","name":"Note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"한 프로퍼티에 저장되거나 다른 클로저로 전달될 때"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"클로저에 있는 객체(self)가 해당 클로저나 넘겨진 클로저에 strong reference를 지닐 때"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"간단한 코드예시"}]}]}]},{"syntax":"swift","code":["class PresentedController: UIViewController {"," var closure: (() -> Void)?","}","","class MainViewController: UIViewController {"," "," var presented = PresentedController() \/\/ PresentedController 소유",""," func setupClosure() {"," presented.closure = printer "," \/\/ 탈출 클로저에 함수를 할당하므로써, "," \/\/ PresentedController가 self(MainViewController)를 소유하게 된다."," }",""," func printer() {"," print(self.view.description)"," }","}","","\/\/ 따라서 이럴때 아래처럼 weak를 할당해주어야 한다.","func setupClosure() {"," self.presented.closure = { [weak self] in "," self?.printer() "," }","}"],"type":"codeListing"},{"name":"Note","type":"aside","content":[{"text":"Delayed Deallocation","level":3,"type":"heading","anchor":"Delayed-Deallocation"}],"style":"note"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Delayed Deallocation은 Escaping 및 Non-escaping 클로저에서 나타나는 부작용이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정확히 메모리 누수는 아니지만 원하지 않는 동작으로 이어질 수 있다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"예시) Controller를 해제했지만, 보류중인 모든 클로저 작업이 완료될 때 까지 메모리가 해제되지 않음"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"기본적으로 클로저는 본문에서 참조하는 모든 객체를 강력하게 캡쳐하기 때문에 클로저 본문이나 범위가 살아있는 한 객체가 메모리에서 할당 해제되는 것을 방해한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"이 경우 Escaping 및 Non-escaping 클로저 모두 [weak self]를 사용하면 Delayed Deallocation을 방지할 수 있다.","type":"text"}],"type":"paragraph"}]}]},{"style":"note","type":"aside","name":"Note","content":[{"type":"heading","text":"Guard let self = self else { return }","anchor":"guard-let-self-=-self-else--return-","level":3}]},{"items":[{"content":[{"inlineContent":[{"text":"Delayed Deallocation가 나타날 수 있는 클로저에서 위 구문을 사용한다면 Delayed Deallocation을 방지할 수 없다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"guard let"},{"text":" 구문이 하는 일은 self가 nil과 같은지 여부를 확인하고, 그렇지 않은 경우 범위 기간 동안 self에 대한 ","type":"text"},{"code":"일시적인 강력한 참조를 생성","type":"codeVoice"},{"text":"하는 것이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉, "},{"type":"codeVoice","code":"guard let"},{"type":"text","text":" 구문은 클로저의 수명 동안 self가 할당 해제되는 것을 방지해 self가 유지되도록 보장한다."}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[UITableView - performBatchUpdate]"}]}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableview\/2887515-performbatchupdates","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Row와 Section을 일괄적으로 업데이트 하는 방법에는 애플의 공식문서에 경우 3가지 메소드를 통해 소개하고 있다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"beginUpdates()","type":"codeVoice"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"endUpdate()","type":"codeVoice"}]}]},{"content":[{"inlineContent":[{"code":"performBatchUpdates(_:completion)","type":"codeVoice"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"공식문서에는 가능한 한 "},{"type":"codeVoice","code":"beginUpdates()"},{"type":"text","text":" "},{"type":"codeVoice","code":"endUpdate()"},{"text":" 대신에 ","type":"text"},{"code":"performBatchUpdates(_:completion)","type":"codeVoice"},{"type":"text","text":" 메소드를 사용하라고 가이드하고 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/thfOYGf.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"여러개의 삽입, 삭제, reload 그리고 이동하는 동작을 그룹으로 애니메이션을 통해 보여준다.","type":"text"}]}]}]},{"code":["func performBatchUpdates(_ updates: (() -> Void)?, "," completion: ((Bool) -> Void)? = nil)"],"type":"codeListing","syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"updates"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"삽입, 삭제, reload, 이동하는 동작을 수행하는 블럭"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테이블의 행의 수정하는 것에 추가로, 이런 수정 사항을 반영하기 위해 테이블의 data source를 업데이트 한다"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 블럭은 return 값이 없고 인자를 가지지 않는다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"completion","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"모든 동작이 끝나면 실행할 블럭"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"code":"finshed","type":"codeVoice"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"애니메이션이 성공적으로 끝났는지를 나타내는 Bool 값"}]}]},{"content":[{"inlineContent":[{"text":"애니메이션이 어떤 이유로든 방해받아서 끝나지 못했다면 false가 된다.","type":"text"}],"type":"paragraph"}]}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이 메서드는 테이블 뷰에 가해지는 여러 변화들이 각각 별도의 애니메이션을 통해 보여지는게 아니라 하나의 애니메이션으로 보여주고 싶을 때 사용한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"하나의 애니메이션으로 보여주고 싶은 동작들을 "},{"type":"codeVoice","code":"update"},{"type":"text","text":" 블럭안에 넣으면 된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고로 삭제하는 동작은 "},{"type":"codeVoice","code":"updates"},{"text":" 내에 코드 순서가 어떻든 간에 항상 삽입하는 동작보다 먼저 처리된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"batch operation 전에 삭제를 위한 인덱스들이 테이블 뷰의 상태에 대한 인덱스를 제공하는 것과 비슷하게 처리되고 삽입에 대한 인덱스들이 모든 삭제하는 동작 이후에 처리된다는 것을 의미하기 때문이다."}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[UITableView - 셀을 스와이프 했을 때 select 유지하기]"}]}]},{"name":"Note","type":"aside","style":"note","content":[{"inlineContent":[{"type":"text","text":"메모장 앱 프로젝트 하는 도중 UITableView의 셀을 스와이프하면 deselect 되는 것을 발견했다. deselect가 자동으로 되는 것 같은데, select를 유지할 수는 없을까?"}],"type":"paragraph"}]},{"type":"paragraph","inlineContent":[{"text":"구글링을 해보니 ","type":"text"},{"type":"reference","isActive":true,"identifier":"https:\/\/stackoverflow.com\/questions\/55220042\/swiping-on-a-tableview-is-deselecting-all-selected-rows-trailingswipeactionsconf"},{"type":"text","text":"에서는 "},{"code":"setEditing","type":"codeVoice"},{"text":" 메소드를 활용해서 해결할 수 있다는 답변이 있었다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableview\/1614876-setediting","type":"reference","isActive":true},{"text":"이라는 메소드가 뭐하는 메소드인지 한번 알아보았다.","type":"text"},{"text":" ","type":"text"},{"text":"디버깅을 해보니 스와이프를 할 때 editing이 true, 스와이프를 취소하거나 종료되었을 때 editing이 false로 전달되고 있었다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"이를 활용해서 select를 유지하는 방법을 구현해보았다."}]},{"items":[{"content":[{"inlineContent":[{"text":"seletedIndex라는 프로퍼티를 선언한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"code":["var selectedIndex: IndexPath?"],"type":"codeListing","syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"그리고 셀이 didSelect 될 때마다 위 프로퍼티에 값을 할당해주도록 한다.","type":"text"}]}]}]},{"code":["override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {"," ..."," selectedIndex = indexPath","}"],"syntax":"swift","type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"이후 setEditing(_:animated:) 메소드를 재정의 해주어 아래와 같은 로직을 추가해주었다.","type":"text"}],"type":"paragraph"}]}]},{"syntax":"swift","code":["override func setEditing(_ editing: Bool, animated: Bool) {"," super.setEditing(editing, animated: animated)"," guard let splitVC = self.splitViewController as? SplitViewController else {"," return"," }"," \/\/ 저장했던 인덱스를 선언"," let selectedIndex = self.selectedIndex ?? IndexPath(row: .zero, section: .zero)"," if editing { \/\/ 편집모드에 들어간다면"," \/\/ 셀을 다시 선택하도록..."," tableView.selectRow(at: selectedIndex, animated: false, scrollPosition: .none)"," } else { \/\/ 편집모드가 종료되었다면"," splitVC.clearNoteTextView() \/\/ 메모영역을 지우고"," guard self.tableView.numberOfRows(inSection: .zero) != .zero else { \/\/ 안전하게 select하기 위한 guard문"," return"," }"," splitVC.present(at: selectedIndex.row) \/\/ 새로운 인덱스의 메모장을 present"," \/\/ 이후 새로운 인덱스로 select되도록 한다."," tableView.selectRow(at: selectedIndex, animated: false, scrollPosition: .none)"," }"," }"],"type":"codeListing"},{"style":"note","name":"Note","type":"aside","content":[{"inlineContent":[{"type":"text","text":"문제"}],"type":"paragraph"}]},{"code":["Thread 1: \"request for rect at invalid index path"],"type":"codeListing","syntax":null},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"선택한 셀보다 위에 위치해있는 셀을 스와이프해서 삭제하였더니 Crash가 발생했다."}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"이유"},{"text":"는 셀 삭제를 처리할 때 위에서 선언해주었던 selectedIndex 변수에 별다른 처리를 해주지 않아서였다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"즉, 셀을 삭제할 때마다 IndexPath의 row의 총 개수가 1씩 줄어드는데, 이를 deleteRows를 할 때 동시에 처리해주지 않았었다."}],"type":"paragraph"}]}]},{"name":"Note","content":[{"inlineContent":[{"text":"해결 방법","type":"text"}],"type":"paragraph"}],"type":"aside","style":"note"},{"type":"paragraph","inlineContent":[{"type":"text","text":"셀을 deleteRows 메소드를 호출하여 삭제할 때, selectedIndex 프로퍼티의 row도 줄어들 수 있도록 1씩 차감해주도록 하였다."}]},{"code":["if let selectedIndex = selectedIndex, selectedIndex >= indexPath {"," \/*"," 만약 저장한 select 인덱스가 삭제하는 인덱스보다 크다면 차감해주어야 크래쉬가 발생안한다."," 이유는 셀이 삭제됨과 동시에 selectedIndex로 selectRow 메소드를 호출해주는데,"," 셀의 총 갯수가 셀이 삭제되면서 차감되므로 deleteRows 이후 다시 정확히 select 하려면"," 1개 차감된, 즉 변동된 row를 select 해야되기 때문이다."," *\/"," self.selectedIndex?.row = selectedIndex.row - 1 > 0 ? selectedIndex.row - 1 : 0"," \/\/ row 값을 차감할 때 -1이 되지 않도록 삼항 연산자 구현"," }"],"type":"codeListing","syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이를 처리해주지 않는다면, 잘못된 인덱스 경로로 셀을 select 하게 되므로 Crash가 발생할 수 있다."}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@haanwave\/Article-You-dont-always-need-weak-self"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/kkimin.tistory.com\/35","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/github.com\/dropbox\/SwiftyDropbox#swift-package-manager","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}],"kind":"content"}],"metadata":{"roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"title":"220215 Dropbox, weak self, escaping, Delayed Deallocation, performBatchUpdate, setEditing","role":"article"},"kind":"article","schemaVersion":{"major":0,"patch":0,"minor":3},"variants":[{"paths":["\/documentation\/arinote\/220215_dropbox,_weak_self,_escaping,_delayed_deallocation,_performbatchupdate,_setediting"],"traits":[{"interfaceLanguage":"swift"}]}],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://developer.apple.com/documentation/uikit/uitableview/1614876-setediting":{"titleInlineContent":[{"text":"setEditing(_:animated:)","type":"text"}],"url":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableview\/1614876-setediting","type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableview\/1614876-setediting","title":"setEditing(_:animated:)"},"https://i.imgur.com/thfOYGf.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/thfOYGf.png"}],"type":"image","identifier":"https:\/\/i.imgur.com\/thfOYGf.png","alt":null},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://github.com/dropbox/SwiftyDropbox/blob/master/Images/InfoPlistExample.png?raw=true":{"variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/dropbox\/SwiftyDropbox\/blob\/master\/Images\/InfoPlistExample.png?raw=true"}],"type":"image","identifier":"https:\/\/github.com\/dropbox\/SwiftyDropbox\/blob\/master\/Images\/InfoPlistExample.png?raw=true","alt":null},"https://stackoverflow.com/questions/55220042/swiping-on-a-tableview-is-deselecting-all-selected-rows-trailingswipeactionsconf":{"titleInlineContent":[{"text":"stackoverflow","type":"text"}],"url":"https:\/\/stackoverflow.com\/questions\/55220042\/swiping-on-a-tableview-is-deselecting-all-selected-rows-trailingswipeactionsconf","type":"link","identifier":"https:\/\/stackoverflow.com\/questions\/55220042\/swiping-on-a-tableview-is-deselecting-all-selected-rows-trailingswipeactionsconf","title":"stackoverflow"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220217_dropbox,_rxswift,_spm,_dispatchgroup.json b/docs/data/documentation/arinote/220217_dropbox,_rxswift,_spm,_dispatchgroup.json new file mode 100644 index 0000000..eef93fd --- /dev/null +++ b/docs/data/documentation/arinote/220217_dropbox,_rxswift,_spm,_dispatchgroup.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Rx"]]},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220217_Dropbox,_RxSwift,_SPM,_DispatchGroup","interfaceLanguage":"swift"},"metadata":{"title":"220217 Dropbox, RxSwift, SPM, DispatchGroup","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article"},"primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","type":"heading","level":1,"text":"TIL (Today I Learned)"},{"inlineContent":[{"text":"2월 17일 (목)","type":"text"}],"type":"paragraph"},{"text":"학습 내용","type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"items":[{"content":[{"inlineContent":[{"text":"동기화 메모장 STEP3 진행","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SwiftyDropbox 사용해보기"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"RxSwift - 개념잡기","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","type":"heading","level":2,"text":"고민한 점 \/ 해결 방법"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[RxSwift - 개념잡기]","type":"text"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"비동기로 생긴 데이터를 어떻게 반환값으로 만들까?"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이런 문제를 해결해주는 유틸리티가 나오게 된다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/MK6zF9J.png","type":"image"}],"type":"paragraph"},{"items":[{"content":[{"level":3,"text":"RxSwift는?","type":"heading","anchor":"RxSwift%EB%8A%94"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"비동기적으로 생기는 데이터를 completion 같은 클로저를 통해서 전달하는 것이 아니라 반환값으로 전달하기 위해 만들어진 유틸리티이다."}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/HeEpNPn.png","type":"image"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Observable 타입으로 감싸서 반환하면 나중에 생기는 데이터가 된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"나중에 생기는 데이터(Observable)을 사용할 때에는 subscribe를 호출하면 된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그럼 거기에 이벤트가 오는데, 종류는 "},{"code":"next","type":"codeVoice"},{"text":", ","type":"text"},{"type":"codeVoice","code":"completed"},{"type":"text","text":", "},{"code":"error","type":"codeVoice"},{"type":"text","text":" 총 3가지이다."}]}]},{"content":[{"inlineContent":[{"text":"데이터가 전달되었을 때는 ","type":"text"},{"code":"next","type":"codeVoice"},{"text":" 케이스로 오고, 데이터가 전달되고 끝났을 때는 ","type":"text"},{"code":"completed","type":"codeVoice"},{"type":"text","text":" 케이스로 온다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"text":"subscribe","level":3,"type":"heading","anchor":"subscribe"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/GjTRynF.png"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"subscribe는 ","type":"text"},{"type":"codeVoice","code":"disposable"},{"type":"text","text":"을 반환하고 있는데, 이게 뭐냐면 동작을 중간에 취소 시킬 수 있는 "},{"code":"dispose()","type":"codeVoice"},{"text":"를 호출할 수 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"위처럼 호출해준다면, 다운로드를 하라고 시켜놓고 dispose를 하게되니 버튼을 눌러도 activityIndicator만 돌아가고, 다운로드는 취소되어 동작하지 않게 된다.","type":"text"}],"type":"paragraph"}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/SKJ0Wap.png"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"⭐️ 여기서 "},{"code":"순환참조 문제","type":"codeVoice"},{"text":"가 발생할 수 있는데, ","type":"text"},{"type":"codeVoice","code":"subscribe"},{"type":"text","text":" 같은 경우에는 "},{"type":"codeVoice","code":"completed"},{"text":"나 ","type":"text"},{"code":"error","type":"codeVoice"},{"text":"에서 ","type":"text"},{"type":"codeVoice","code":"클로저가 종료"},{"text":"되기 때문에, 작업후 작업이 완료되었다고 알려주게 되면(","type":"text"},{"type":"codeVoice","code":"onCompleted()"},{"type":"text","text":") 해당 문제는 해결된다."}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[배울 것]","type":"text"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"비동기로 생기는 데이터를 ","type":"text"},{"code":"Observable","type":"codeVoice"},{"type":"text","text":"로 "},{"type":"codeVoice","code":"감싸서 리턴"},{"type":"text","text":"하는 방법"}],"type":"paragraph"}]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/TnCVUny.png","type":"image"}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["func rxswiftLoadImage(from imageUrl: String) -> Observable {"," return Observable.create { seal in"," asyncLoadImage(from: imageUrl) { image in"," seal.onNext(image)"," seal.onCompleted()"," }"," return Disposables.create()"," }"," }"]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/66rmTfw.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Observable.create()"},{"type":"text","text":" 만들고 들어가는 인자로 클로저가 하나 들어가는데, 뭔가("},{"type":"codeVoice","code":"emitter"},{"text":")가 들어간다. 그리고 나서 클로저 내부에 ","type":"text"},{"type":"codeVoice","code":"onNext()"},{"type":"text","text":" 메소드로 데이터를 전달한다. 데이터는 "},{"code":"여러개를 전달","type":"codeVoice"},{"type":"text","text":"할 수도 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이후 ","type":"text"},{"type":"codeVoice","code":"onCompleted()"},{"text":"로 데이터 전달이 끝났다고 알린 후 반환한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그리고 마지막으로 ","type":"text"},{"code":"Disposables.create()","type":"codeVoice"},{"type":"text","text":"를 "},{"type":"codeVoice","code":"호출하여 반환"},{"type":"text","text":"하면 된다."}]}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/o1BazFV.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"Observabel.create()"},{"type":"text","text":"를 한다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"task","type":"codeVoice"},{"type":"text","text":"를 만들어서 "},{"code":"dataTask를 호출","type":"codeVoice"},{"text":"하고, 에러가 발생한다면 ","type":"text"},{"code":"onError로 에러를 전달","type":"codeVoice"},{"text":"하고, data가 정상적으로 바인딩 된다면 ","type":"text"},{"type":"codeVoice","code":"onNext로 데이터를 전달"},{"type":"text","text":"하고 "},{"code":"onCompleted()를 실행","type":"codeVoice"},{"text":"한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이후 return에서 Disposables.create()를 호출할 때 "},{"code":"task.cancel()","type":"codeVoice"},{"text":"을 호출해준다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"아마 dispose를 했을 때 호출 되는 구현부인 듯 하다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Observable의 생명주기","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"create"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"subscribe","type":"codeVoice"},{"text":" <- 이때 Observable로 create한게 동작한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"onNext"}]}]},{"content":[{"inlineContent":[{"code":"onCompleted","type":"codeVoice"},{"text":" \/ onError","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Disposed"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"이렇게 동작이 끝난 ","type":"text"},{"type":"codeVoice","code":"Observable"},{"text":"은 다시 ","type":"text"},{"type":"strong","inlineContent":[{"type":"text","text":"✨재사용할 수 없다.✨"}]}],"type":"paragraph"}]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/IRfHnAr.png","type":"image"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"어떻게 동작하는지는 중간에 debug를 호출해주면 로그를 확인할 수 있다."}]}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/5FPRnFz.png"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Observable로 오는 데이터를 받아서 처리하는 방법"}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/3lfeneK.jpg"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"subscribe","type":"codeVoice"},{"text":"는 클로저를 개행하면 ","type":"text"},{"type":"codeVoice","code":"이벤트를 처리"},{"type":"text","text":"할 수 있고, subscribe() 호출만 한다면 값만 전달받을 수도 있다."}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[SPM으로 라이브러리 추가하기]"}],"type":"strong"}]},{"type":"aside","style":"note","content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Targets"},{"text":" -> ","type":"text"},{"type":"codeVoice","code":"General"},{"type":"text","text":" -> "},{"type":"codeVoice","code":"Frameworks, Libraries, and Embedded Content"},{"text":" -> ","type":"text"},{"type":"codeVoice","code":"+"}]}],"name":"Note"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/MxRxY4R.png"}],"type":"paragraph"},{"name":"Note","content":[{"inlineContent":[{"code":"Add Package Dependency...","type":"codeVoice"},{"text":" 를 클릭","type":"text"}],"type":"paragraph"}],"type":"aside","style":"note"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/3iPfMsZ.png","type":"image"}]},{"type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용하고 싶은 라이브러리의 주소를 기입한다."}]}],"name":"Note","style":"note"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/YGUZll3.png"}]},{"type":"aside","content":[{"type":"paragraph","inlineContent":[{"text":"설치 시 원하는 버전, 브랜치 및 커밋을 설정할 수 있다. 이 후 원하는 packge product를 골라서 Finish 까지 하면…","type":"text"}]}],"name":"Note","style":"note"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/ejnhQOL.png","type":"image"}]},{"name":"Note","type":"aside","content":[{"inlineContent":[{"code":"SwiftyDropbox","type":"codeVoice"},{"text":"가 정상적으로 설치된 것을 확인할 수 있다.","type":"text"}],"type":"paragraph"}],"style":"note"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[SwiftyDropbox - 프로젝트 설정하기]"}],"type":"strong"}]},{"style":"note","type":"aside","name":"Note","content":[{"inlineContent":[{"text":"아래 프로젝트 설정하는 튜토리얼을 참고하여 진행하였다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"https:\/\/github.com\/dropbox\/SwiftyDropbox#configure-your-project"}],"type":"paragraph"}]},{"type":"aside","style":"note","content":[{"inlineContent":[{"type":"text","text":"먼저 Info.plist 파일을 수정해주어야 하는데, 그 전에 dropbox에 app을 등록해야 한다. 로그인 후 apps에 들어가면 아래와 같은 버튼이 있다."}],"type":"paragraph"}],"name":"Note"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/xBg2zZc.png","type":"image"}],"type":"paragraph"},{"content":[{"inlineContent":[{"type":"text","text":"이후 필수 문항을 선택, 입력 후 create app 버튼을 눌러 만들어주면 된다."}],"type":"paragraph"}],"style":"note","name":"Note","type":"aside"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/mqSylZ5.png"}]},{"type":"aside","content":[{"inlineContent":[{"type":"text","text":"그러면 App key가 발급되는데, 이걸 이제 Info.plist를 수정하는데 활용할 것이다."}],"type":"paragraph"}],"name":"Note","style":"note"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/QuHdJk5.png"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"튜토리얼에서 하라는데로 "},{"code":"Info.plist","type":"codeVoice"},{"text":"를 예시와 같이 수정해준다.","type":"text"}]},{"code":["LSApplicationQueriesSchemes<\/key>"," "," dbapi-8-emm<\/string>"," dbapi-2<\/string>"," <\/array>"],"type":"codeListing","syntax":null},{"type":"aside","name":"Note","content":[{"inlineContent":[{"type":"text","text":"아까 만들고 얻은 App key를 "},{"type":"codeVoice","code":"db-"},{"text":" 뒤부터 기입해주면 된다.","type":"text"}],"type":"paragraph"}],"style":"note"},{"syntax":null,"type":"codeListing","code":["CFBundleURLTypes<\/key>"," "," "," CFBundleURLSchemes<\/key>"," "," db-<\/string>"," <\/array>"," CFBundleURLName<\/key>"," <\/string>"," <\/dict>"," <\/array>"]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/ltONNs1.png","type":"image"}]},{"content":[{"inlineContent":[{"type":"text","text":"이후 코드로 돌아가서 "},{"code":"AppDelegate","type":"codeVoice"},{"type":"text","text":"에 "},{"type":"codeVoice","code":"DropboxClient"},{"type":"text","text":" 인스턴스를 초기화 해준다."}],"type":"paragraph"}],"type":"aside","name":"Note","style":"note"},{"code":["import SwiftyDropbox","","func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {"," DropboxClientsManager.setupWithAppKey(\"\")"," return true","}"],"type":"codeListing","syntax":"swift"},{"style":"note","type":"aside","name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 "},{"type":"codeVoice","code":"SceneDelegate"},{"type":"text","text":"에 아래와 같은 메소드를 추가한다. 인증이 모두 완료된 후 redirection을 처리해준다."}]}]},{"type":"codeListing","syntax":"swift","code":["import SwiftyDropbox","","func scene(_ scene: UIScene, openURLContexts URLContexts: Set) {"," let oauthCompletion: DropboxOAuthCompletion = {"," if let authResult = $0 {"," switch authResult {"," case .success:"," print(\"Success! User is logged into DropboxClientsManager.\")"," case .cancel:"," print(\"Authorization flow was manually canceled by user!\")"," case .error(_, let description):"," print(\"Error: \\(String(describing: description))\")"," }"," }"," }",""," for context in URLContexts {"," \/\/ stop iterating after the first handle-able url"," if DropboxClientsManager.handleRedirectURL(context.url, completion: oauthCompletion) { break }"," }","}"," }"]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이후 맨처음에 시작하는 뷰에 로그인을 해서 인증 토큰을 받아오는 작업을 추가한다. 이번 프로젝트 같은 경우 UISplitViewController를 사용했는데, rootView인 SplitViewController에서는 해당 작업이 정상적으로 뜨지않았다. (이유는 찾지 못했다.) 그래서 다른 UIViewController에서 진행해야하나.. 싶어서 UITableViewController의 viewDidLoad()에서 해당 작업을 실행해주니 로그인창이 정상적으로 떴다."}]}],"style":"note","name":"Note","type":"aside"},{"type":"codeListing","syntax":"swift","code":["import SwiftyDropbox","","func myButtonInControllerPressed() {"," \/\/ OAuth 2 code flow with PKCE that grants a short-lived token with scopes, and performs refreshes of the token automatically."," let scopeRequest = ScopeRequest(scopeType: .user, scopes: [\"account_info.read\"], includeGrantedScopes: false)"," DropboxClientsManager.authorizeFromControllerV2("," UIApplication.shared,"," controller: self,"," loadingStatusDelegate: nil,"," openURL: { (url: URL) -> Void in UIApplication.shared.open(url, options: [:], completionHandler: nil) },"," scopeRequest: scopeRequest"," )",""," \/\/ Note: this is the DEPRECATED authorization flow that grants a long-lived token."," \/\/ If you are still using this, please update your app to use the `authorizeFromControllerV2` call instead."," \/\/ See https:\/\/dropbox.tech\/developers\/migrating-app-permissions-and-access-tokens"," DropboxClientsManager.authorizeFromController(UIApplication.shared,"," controller: self,"," openURL: { (url: URL) -> Void in"," UIApplication.shared.open(url, options: [:], completionHandler: nil)"," })","}"]},{"name":"Note","type":"aside","style":"note","content":[{"inlineContent":[{"type":"text","text":"여기서 scopes라는 파라미터가 있는데, 이 부분은 앱이 Dropbox 계정 정보를 보고 관리할 수 있도록 "},{"code":"권한의 범위","type":"codeVoice"},{"type":"text","text":"를 뜻한다. 아까 App key를 얻었던 곳에서 "},{"code":"Permissions","type":"codeVoice"},{"text":" 탭을 클릭하면 ","type":"text"},{"type":"codeVoice","code":"Account의 정보"},{"type":"text","text":"가 나온다. 따라서 필요한 Account를 "},{"code":"scopes","type":"codeVoice"},{"text":"에 넣어주면 되겠다.","type":"text"}],"type":"paragraph"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/1O8bJws.jpg"}]},{"name":"Note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 다음에 API에 호출할 "},{"type":"codeVoice","code":"DropboxClient 인스턴스"},{"text":"를 생성한다.","type":"text"}]}],"style":"note"},{"code":["import SwiftyDropbox","","\/\/ Reference after programmatic auth flow","let client = DropboxClientsManager.authorizedClient"],"type":"codeListing","syntax":"swift"},{"style":"note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"client"},{"text":"를 통해 ","type":"text"},{"type":"codeVoice","code":"업로드"},{"type":"text","text":"와 "},{"type":"codeVoice","code":"다운로드"},{"text":"를 진행할 수 있다.","type":"text"}]}],"name":"Note"},{"code":["let fileData = \"testing data example\".data(using: String.Encoding.utf8, allowLossyConversion: false)!","","let request = client.files.upload(path: \"\/test\/path\/in\/Dropbox\/account\", input: fileData)"," .response { response, error in"," if let response = response {"," print(response)"," } else if let error = error {"," print(error)"," }"," }"," .progress { progressData in"," print(progressData)"," }","","\/\/ in case you want to cancel the request","if someConditionIsSatisfied {"," request.cancel()","}"],"syntax":"swift","type":"codeListing"},{"syntax":"swift","code":["\/\/ Download to URL","let fileManager = FileManager.default","let directoryURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]","let destURL = directoryURL.appendingPathComponent(\"myTestFile\")","let destination: (URL, HTTPURLResponse) -> URL = { temporaryURL, response in"," return destURL","}","client.files.download(path: \"\/test\/path\/in\/Dropbox\/account\", overwrite: true, destination: destination)"," .response { response, error in"," if let response = response {"," print(response)"," } else if let error = error {"," print(error)"," }"," }"," .progress { progressData in"," print(progressData)"," }","","","\/\/ Download to Data","client.files.download(path: \"\/test\/path\/in\/Dropbox\/account\")"," .response { response, error in"," if let response = response {"," let responseMetadata = response.0"," print(responseMetadata)"," let fileContents = response.1"," print(fileContents)"," } else if let error = error {"," print(error)"," }"," }"," .progress { progressData in"," print(progressData)"," }"],"type":"codeListing"},{"name":"Note","style":"note","content":[{"inlineContent":[{"type":"text","text":"두가지의 공통점은 파일을 다운로드하고, 업로드할 때 "},{"type":"codeVoice","code":"경로"},{"text":"가 필요하다는 점이다. 메모장 프로젝트의 경우 CoreData를 통해서 메모를 관리하고 있기 때문에 ","type":"text"},{"code":"백업","type":"codeVoice"},{"type":"text","text":"의 형태로 CoreData의 경로를 얻어내서 "},{"type":"codeVoice","code":".sqlite"},{"text":", ","type":"text"},{"type":"codeVoice","code":".sqlite-shm"},{"type":"text","text":", "},{"type":"codeVoice","code":".sqlite-wal"},{"type":"text","text":" 총 3개의 파일을 업로드 및 다운로드 해주도록 구현해주었다."}],"type":"paragraph"}],"type":"aside"},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"업로드, 다운로드 모두 파일을 덮어쓸건지에 대한 옵션이 있으니 자세한건 아래 도큐먼트에서 검색해보면 되겠다."}]}],"style":"note","type":"aside","name":"Note"},{"type":"paragraph","inlineContent":[{"text":"https:\/\/dropbox.github.io\/SwiftyDropbox\/api-docs\/latest\/index.html","type":"text"}]},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[SwiftyDropbox - download가 끝나는 시점에 뷰를 업데이트 하기]"}],"type":"strong"}],"type":"paragraph"},{"type":"aside","name":"Note","content":[{"inlineContent":[{"type":"text","text":"다운로드가 끝난 후 CoreData를 fetch를 하고 TableView를 reload를 해주고 싶었으나 실패했었다."}],"type":"paragraph"}],"style":"note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"이유"},{"text":" 파일이 여러개가 존재하여, 여러개의 파일을 다운로드 하기 위해 반복문을 돌리고 있었으나, fetch와 reload를 for-in문 내부에서 해주고 있어서, 뷰가 업데이트 될 때가 있고, 안되기도 하는 현상이 나타났다.","type":"text"}]}]},{"content":[{"inlineContent":[{"code":"해결","type":"codeVoice"},{"type":"text","text":" 그래서 "},{"type":"codeVoice","code":"for-in문이 종료된 시점"},{"type":"text","text":"에 "},{"code":"fetch","type":"codeVoice"},{"type":"text","text":"를 하고 view를 reload를 해주기 위해, 다운로드가 모두 완료되는 시점을 "},{"code":"DispatchGroup","type":"codeVoice"},{"type":"text","text":"를 활용하여 "},{"type":"codeVoice","code":"추적"},{"type":"text","text":"하고, 반복문에서 시작되었던 다운로드 작업이 모두 끝나게 되면 아래 뷰를 다시 설정하도록 코드를 수정하였다."}],"type":"paragraph"}]}]},{"type":"codeListing","syntax":"swift","code":["func download(_ tableViewController: NotesViewController?) {"," let group = DispatchGroup() \/\/ 그룹 생성"," for fileName in fileNames {"," let destURL = applicationSupportDirectoryURL.appendingPathComponent(fileName)"," let destination: (URL, HTTPURLResponse) -> URL = { _, _ in"," return destURL"," }"," group.enter() \/\/ 작업 시작"," client?.files.download(path: fileName, overwrite: true, destination: destination)"," .response { _, error in"," if let error = error {"," print(error)"," }"," group.leave() \/\/ 작업 끝"," }"," }"," group.notify(queue: .main) { \/\/ 모든 작업이 끝난다면 ..."," PersistentManager.shared.setUpNotes()"," tableViewController?.tableView.reloadData()"," tableViewController?.stopActivityIndicator()"," }","}"]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/www.youtube.com\/watch?v=iHKBNYMWd5I"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/github.com\/dropbox\/SwiftyDropbox"}]}]}]}]}]}]}],"kind":"article","sections":[],"schemaVersion":{"major":0,"minor":3,"patch":0},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220217_dropbox,_rxswift,_spm,_dispatchgroup"]}],"references":{"https://i.imgur.com/o1BazFV.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/o1BazFV.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/o1BazFV.png","traits":["1x","light"]}]},"https://i.imgur.com/SKJ0Wap.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/SKJ0Wap.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/SKJ0Wap.png","traits":["1x","light"]}]},"https://i.imgur.com/YGUZll3.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/YGUZll3.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/YGUZll3.png","traits":["1x","light"]}]},"https://i.imgur.com/MK6zF9J.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/MK6zF9J.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/MK6zF9J.png","traits":["1x","light"]}]},"https://i.imgur.com/ejnhQOL.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/ejnhQOL.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/ejnhQOL.png","traits":["1x","light"]}]},"https://i.imgur.com/66rmTfw.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/66rmTfw.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/66rmTfw.png","traits":["1x","light"]}]},"https://i.imgur.com/5FPRnFz.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/5FPRnFz.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/5FPRnFz.png","traits":["1x","light"]}]},"https://i.imgur.com/QuHdJk5.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/QuHdJk5.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/QuHdJk5.png","traits":["1x","light"]}]},"https://i.imgur.com/ltONNs1.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/ltONNs1.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/ltONNs1.png","traits":["1x","light"]}]},"https://i.imgur.com/HeEpNPn.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/HeEpNPn.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/HeEpNPn.png","traits":["1x","light"]}]},"https://i.imgur.com/MxRxY4R.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/MxRxY4R.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/MxRxY4R.png","traits":["1x","light"]}]},"https://i.imgur.com/GjTRynF.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/GjTRynF.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/GjTRynF.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote/Rx":{"type":"topic","abstract":[{"type":"text","text":"RxSwift를 활용한 비동기 프로그래밍"}],"role":"collectionGroup","url":"\/documentation\/arinote\/rx","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Rx","title":"RxSwift","kind":"article"},"https://i.imgur.com/IRfHnAr.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/IRfHnAr.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/IRfHnAr.png","traits":["1x","light"]}]},"https://i.imgur.com/TnCVUny.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/TnCVUny.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/TnCVUny.png","traits":["1x","light"]}]},"https://i.imgur.com/3iPfMsZ.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/3iPfMsZ.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/3iPfMsZ.png","traits":["1x","light"]}]},"https://i.imgur.com/1O8bJws.jpg":{"type":"image","identifier":"https:\/\/i.imgur.com\/1O8bJws.jpg","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/1O8bJws.jpg","traits":["1x","light"]}]},"https://i.imgur.com/mqSylZ5.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/mqSylZ5.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/mqSylZ5.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/xBg2zZc.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/xBg2zZc.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/xBg2zZc.png","traits":["1x","light"]}]},"https://i.imgur.com/3lfeneK.jpg":{"type":"image","identifier":"https:\/\/i.imgur.com\/3lfeneK.jpg","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/3lfeneK.jpg","traits":["1x","light"]}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220218_rxswift,_rxcocoa,_rxrelay,_mvvm.json b/docs/data/documentation/arinote/220218_rxswift,_rxcocoa,_rxrelay,_mvvm.json new file mode 100644 index 0000000..bab9936 --- /dev/null +++ b/docs/data/documentation/arinote/220218_rxswift,_rxcocoa,_rxrelay,_mvvm.json @@ -0,0 +1 @@ +{"metadata":{"role":"article","roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"title":"220218 RxSwift, RxCocoa, RxRelay, MVVM"},"schemaVersion":{"major":0,"minor":3,"patch":0},"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220218_RxSwift,_RxCocoa,_RxRelay,_MVVM","interfaceLanguage":"swift"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Rx"]]},"sections":[],"variants":[{"paths":["\/documentation\/arinote\/220218_rxswift,_rxcocoa,_rxrelay,_mvvm"],"traits":[{"interfaceLanguage":"swift"}]}],"primaryContentSections":[{"content":[{"text":"TIL (Today I Learned)","level":1,"anchor":"TIL-Today-I-Learned","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"2월 18일 (금)"}]},{"type":"heading","text":"학습 내용","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"RxSwift","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Sugar API"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"RxCocoa"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"RxRelay","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"MVVM 적용시켜보기"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"heading","text":"고민한 점 \/ 해결 방법","level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[RxSwift - Sugar API]"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"간단한 생성 : ","type":"text"},{"code":"just","type":"codeVoice"},{"text":", ","type":"text"},{"code":"from","type":"codeVoice"}]}]},{"content":[{"inlineContent":[{"text":"필터링 : ","type":"text"},{"type":"codeVoice","code":"filter"},{"text":", ","type":"text"},{"code":"take","type":"codeVoice"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"데이터 변형 : ","type":"text"},{"code":"map","type":"codeVoice"},{"type":"text","text":", "},{"code":"flatMap","type":"codeVoice"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그 외 : A Decision Tree of Observable Operators","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Marble Diagram","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"http:\/\/rxmarbles.com\/"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"http:\/\/reactivex.io\/documentation\/operators.html","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/itunes.apple.com\/us\/app\/rxmarbles\/id1087272442?mt=8","type":"text"}]}]}],"type":"unorderedList"}]}]},{"name":"Note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"text":"Sugar API는 기본사용법의 추가적인 귀찮은 것들을 없애주는 사용법이다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":"이러한 것들을 오퍼레이터라고 부른다."}]}],"style":"note"},{"text":"just","level":3,"type":"heading","anchor":"just"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"데이터를 하나만 전달할 때 간단히 사용할 수 있다.","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/bDymOxA.png","type":"image"}]},{"level":3,"type":"heading","anchor":"from","text":"from"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"데이터를 하나씩 여러개 전달하고 싶을 때 사용한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그럼 next에 Hello 한번, World 두번 총 두개의 데이터가 차례대로 전달된다."}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/lXZIWVC.png"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Pts6L5X.png"}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/STrQA41.png","type":"image"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"subscribe에도 sugar가 존재한다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이런식으로 단순 파라미터로도 전달받을 수 있다.","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"anchor":"observeOn","type":"heading","level":3,"text":"observeOn"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"메인 스레드에서 처리해야하는 UI작업은 DispatchQueue.main으로 감싸줘야하는데, 해당 작업을 생략하고 싶을 때 사용한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터를 중간에 바꾸는 sugar의 형태인데, 이런 것들을 operator라고 한다."}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/ezu4nj3.png"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/xMyfsQH.png"}]},{"text":"map","type":"heading","anchor":"map","level":3},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"데이터를 중간에서 변형해주고 싶을 때 사용한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"아래는 Int로 변형했다가 다시 String으로 변환하는 예제이다."}]}]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/fFQsQNo.png","type":"image"}],"type":"paragraph"},{"type":"heading","text":"filter","level":3,"anchor":"filter"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"조건을 걸어 json의 카운트가 0보다 클 경우에 아래 클로저를 마저 실행한다."}]}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/BHsYbEV.png"}]},{"type":"aside","content":[{"inlineContent":[{"type":"text","text":"그 외 수많은 오퍼레이터들…."},{"type":"text","text":" "},{"type":"text","text":"https:\/\/reactivex.io\/documentation\/ko\/operators.html"}],"type":"paragraph"}],"name":"Note","style":"note"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"마블 다이어그램을 통해 설명되어있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"text":"ObserveOn","level":3,"anchor":"ObserveOn","type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Observable 유틸리티 오퍼레이터"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"스레드를 지정해주는 오퍼레이터이다."}],"type":"paragraph"}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/u7M2N7r.png"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"ObserveOn을 하는 순간 줄의 색이 변경되는데, 이 줄은 스레드를 의미한다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"subscribeOn은 시작시점의 스레드에 영향을 주는 유틸리티"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"위치랑 상관없이 어디에 있든, 시작시점의 스레드를 전환해준다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"위 그림을 참고해보면 다중 스레드 환경에서 스레드 간의 Observable을 전환하는 모습을 볼 수 있다.","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/N3aZ9Pa.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"코드로 예시를 보자."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"subscribeOn으로 시작시점 스레드를 Concurrent으로 생성해주었다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그러면 map, filter, map의 작업들은 모두 Concurrent에서 이루어진다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이후 observeOn이 호출이 되는데, 그 아래에 있는 subscribe의 클로저는 메인스레드에서 처리된다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"알파벳 순으로 정렬된 연산자의 리스트를 보면 어떤건 굵게 표시되어있는 경우가 있다.","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/iFteEbO.png"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"굵게 표시된 것은 "},{"type":"codeVoice","code":"대표 기능 연산자"},{"type":"text","text":"이기 때문이다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"level":3,"text":"Merge","type":"heading","anchor":"Merge"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"여러개의 Observable을 묶어서 하나의 Observable로 만들어준다."}],"type":"paragraph"}]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/5UXT0A0.png","type":"image"}],"type":"paragraph"},{"level":3,"anchor":"Zip","type":"heading","text":"Zip"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"위 아래로 데이터가 하나씩 생성되면 그걸 쌍으로 만들어서 합쳐준다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터가 생성되는 시점이 서로 달라도 알아서 쌍으로 만들어준다."}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/sjYe0mD.png","type":"image"}]},{"type":"heading","level":3,"text":"CombineLatest","anchor":"CombineLatest"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Zip과 다르게 짝이 안맞아도 된다. 가장 최근에 나온 데이터와 함께 쌍으로 만들어서 합쳐준다."}]}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/vL8l4CS.png"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"코드 예제","type":"text"}]}],"name":"Note","style":"note","type":"aside"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/brz1tWB.png"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Observable을 두개 선언해주고 zip을 통해 쌍으로 묶어준다. 이후 클로저에는 두 쌍을 linebreak와 함께 병합해주고 있다."}],"type":"paragraph"}]}]},{"style":"note","type":"aside","content":[{"inlineContent":[{"text":"disposable을 활용하는 방법","type":"text"}],"type":"paragraph"}],"name":"Note"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/VwlIpHE.png"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"위 예제처럼 disposable이라는 프로퍼티를 만들어주고, viewWillDisappear가 호출되는 시점에 만약 다운로드를 받고있는 중이라면, 중간에 네트워킹을 취소시켜줄 수가 있다.","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/QlqeOOb.png"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"또다른 응용 방법은 disposable을 배열로 선언하고, Observable을 subscribe을 해줄 때마다 생성되는 disposable을 append를 시켜주고, 나중에 반복문을 돌면서 취소시켜주는 방법도 있다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":"이 부분도 Sugar가 존재하는데, DisposeBag이라는 오퍼레이터가 있다."}]},{"level":3,"type":"heading","text":"DisposeBag","anchor":"DisposeBag"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"말 그대로 disposable을 담는 가방이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 특별히 dispose()를 호출해주지 않아도 자동으로 작업을 취소시켜준다."}]}]},{"content":[{"inlineContent":[{"text":"insert를 호출해서 disposable을 추가해줄 수 있다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/IUJfjh1.png"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"이렇게 마지막에 disposed(by:) 를 호출해서 가방에 넣어주는 방법도 존재한다."}]},{"type":"aside","content":[{"inlineContent":[{"type":"text","text":"클로저와 메모리 해제 실험 중…"}],"type":"paragraph"}],"style":"note","name":"Note"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"self를 맘껏 사용하지만 메모리 해제가 잘 되는 예제를 만들어보자","type":"text"}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/JY8cyD4.png"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"뷰가 메모리에서 해제되는 시점에서 disposeBag도 같이 사라지지만, 참조카운트가 남아있다면 disposeBag이 사라지지 않는 경우도 존재한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 뷰가 사라질 때 dispose를 강제시키면 메모리 해제를 통제할 수 있게 된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉 순환참조도 예방할 수 있게 된다."}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[RxSwift - 활용범위 넓히기, UI 컴포넌트와의 연동]","type":"text"}],"type":"strong"}]},{"anchor":"RxCocoa","text":"RxCocoa","type":"heading","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"RxSwift의 기능을 UiKit에 extension으로 추가한게 RxCocoa 라고 한다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"뷰에 보여줄 ViewModel 타입을 만든다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"그리고 뷰에 보여줄 데이터들을 모조리 ViewModel 타입안에 때려박는다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"뷰컨트롤러에는 viewModel이라는 프로퍼티를 선언해준다.","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"text":"Subject","level":3,"type":"heading","anchor":"Subject"},{"style":"note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Observable은 값을 넘겨주는 역할을 하지, 값을 외부에서 받아들여서 넘겨주는 역할은 하지 않는다. 그래서 문제가 생겼는데… ‘Observable 처럼 값을 받아먹을 수는 있는 애인데, 외부에서 이 값을 컨트롤할 순 없을까?’ 그래서 나온 것이 Subject이다."}]}],"name":"Note","type":"aside"},{"items":[{"content":[{"inlineContent":[{"text":"Observable과 다르게 값을 받아먹을 수도 있지만, 외부에서 값을 통제할 수도 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"총 4가지의 종류가 있다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"AsyncSubject","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"여러개가 구독을 하고 있더라도 다 안내려보내준다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그러다가 completes되는 시점에 가장 마지막에 있던 거를 모든 애들한태 다 내려주고 complete을 시킨다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"BehaviorSubject","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"기본값을 가지고 시작한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"아직 데이터가 생성되지 않았을 때 누군가가 subscribe를 하자마자 기본값을 내려준다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그리고 데이터가 생기면 그때마다 계속 내려준다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"새로운 게 중간에 subscribe를 하고나면 가장 최근에 발생했던 값을 일단 내려주고나서 그 다음부터 발생하는 데이터를 똑같이 모든 구독하는 애들한태 내려보내준다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"PublishSubject","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"subscribe를 하면 데이터를 그대로 내려보내준다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"다른 subscribe가 또 새롭게 subscribe 할 수 있다. 그럼 또 데이터가 생성된다면 subscribe하고 있는 모든 관찰자한태 데이터를 내려준다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"code":"ReplaySubject","type":"codeVoice"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"subscribe를 했을 때 그대로 순서대로 데이터를 내려보내준다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"두번째로 subscribe를 한다면 여태까지 발생했던 모든 데이터를 다 내려준다. 한꺼번에 Replay를 하는 것이다.","type":"text"}]}]}]}]}]}]}],"type":"unorderedList"},{"style":"note","name":"Note","content":[{"inlineContent":[{"text":"코드예제","type":"text"}],"type":"paragraph"}],"type":"aside"},{"type":"codeListing","syntax":"swift","code":["viewModel.totalPrice"," .scan(0, accumulator: +) \/\/ 0부터 시작해서 새로운 값이 들어오면 기존에 있던 값이랑 더한다."," .map { $0.currencyKR() } \/\/ totalPrice를 가져와서 String으로 변환해주고"," .subscribe(onNext: {"," self.totalPrice.text = $0 \/\/ UI를 업데이트 한다."," })"," .disposed(by: disposeBag) \/\/ 그리고 dispose 가방에 담는다."]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이전에는 UI를 업데이트하는 메소드를 만들어서, 필요한 시점마다 호출시켜주어야 UI가 바뀌었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하지만 위와같이 맨처음 한번만 subscribe를 호출시켜도, subscribe를 한거에 의해서 값이 바뀔 때마다 UI를 업데이트 하도록 한다."}]}]}]},{"code":["class MenuListViewModel {","\/\/ var dummyMenus: [Menu] = [","\/\/ Menu(name: \"튀김1\", price: 100, count: 0),","\/\/ Menu(name: \"튀김2\", price: 100, count: 0),","\/\/ Menu(name: \"튀김3\", price: 100, count: 0),","\/\/ Menu(name: \"튀김4\", price: 100, count: 0),","\/\/ Menu(name: \"튀김5\", price: 100, count: 0),","\/\/ Menu(name: \"튀김6\", price: 100, count: 0)","\/\/ ]"," "," \/\/ var itemsCount: Int = 5"," \/\/ var totalPrice: Int = 10_000"," \/\/ var totalPrice: PublishSubject = PublishSubject()"," \/\/ MARK: - 개선된 버전"," lazy var menuObservable = PublishSubject<[Menu]>()"," "," lazy var itemsCount = menuObservable.map {"," $0.map { $0.count }.reduce(0, +)"," }"," lazy var totalPrice = menuObservable.map {"," $0.map { $0.price * $0.count }.reduce(0, +)"," }"," "," init() {"," let dummyMenus: [Menu] = ["," Menu(name: \"튀김1\", price: 100, count: 0),"," Menu(name: \"튀김2\", price: 100, count: 0),"," Menu(name: \"튀김3\", price: 100, count: 0),"," Menu(name: \"튀김4\", price: 100, count: 0),"," Menu(name: \"튀김5\", price: 100, count: 0),"," Menu(name: \"튀김6\", price: 100, count: 0)"," ]"," "," menuObservable.onNext(dummyMenus)"," }","}"],"type":"codeListing","syntax":"swift"},{"style":"note","type":"aside","name":"Note","content":[{"inlineContent":[{"text":"위 예제코드 설명","type":"text"}],"type":"paragraph"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"배열을 담고 있는 "},{"code":"PublishSubject","type":"codeVoice"},{"type":"text","text":"를 생성한다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"외부로부터 배열이 주어지면 그때마다 Observable이 동작할 것이다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"itemsCount","type":"codeVoice"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"menuObservable를 바라보면서 뭔가 값이 바뀔 때마다 아이템의 총 합을 카운트로 내려보내주는 Observable이다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"code":"totalPrice","type":"codeVoice"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"menuObservable을 바라보면서 값이 바뀔 때마다 가격의 총합을 바꿔서 내려보내주는 Observable이다.","type":"text"}]}]}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 ViewController에서는 아래 코드를 딱 한번만 호출해주면 된다는 것이다."}]},{"syntax":"swift","code":["viewModel.itemsCount"," .map { \"\\($0)\" } \/\/ 문자열로 바까서"," .subscribe(onNext: { \/\/ 구독할게~~~"," self.itemCountLabel.text = $0 \/\/ 값이 바뀔 때마다 UI 업데이트 해라..."," })"," .disposed(by: disposeBag) \/\/ 가방에 담아두기"],"type":"codeListing"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[RxSwift를 활용한 아키텍처 - 프로젝트에 MVVM 적용해보기]"}],"type":"strong"}]},{"style":"note","content":[{"inlineContent":[{"text":"RxCocoa는 RxSwift 요소들을 UiKit을 extension하여 접목시킨 것이다.","type":"text"}],"type":"paragraph"}],"name":"Note","type":"aside"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/LLsMZXh.png","type":"image"}]},{"items":[{"content":[{"inlineContent":[{"text":"이런 rx를 제공해준다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Binder는 Bind를 시킬 수 있는데, subscribe를 하지 않고, binder를 넣어줄 수 있다."}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/aBIS3B0.png"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"bind와 subscribe는 같은일을 하는거 같은데…?"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/BYS4xQI.png"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"그렇다. bind는 subscribe의 "},{"type":"codeVoice","code":"Sugar"},{"text":"다. 3줄에 걸쳐서 표현된 것이 1줄에 끝난다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"bind를 사용하면 장점이 있는데, subscribe의 경우 순환참조를 방지하려면 weak self 키워드를 사용해야하는데, 반면에 bind를 사용하면 알아서 순환참조를 예방해준다."}]}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/l8yu9Ur.png"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이런 extension을 제공해주는 것이 RxCocoa이다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":"그리고 UI를 업데이트할 땐 메인스레드에서 안전하게 처리해주도록 ","type":"text"},{"code":"observeOn","type":"codeVoice"},{"text":"을 추가해주어야 한다.","type":"text"}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/DAe1iiG.png"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"tableView 같은 경우에도 배열에 bind를 아래와 같이 추가해줄 수 있는데,"}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/6K4NidU.png","type":"image"}],"type":"paragraph"},{"type":"codeListing","code":["viewModel.menuObservable"," .bind(to: tableView.rx.items("," cellIdentifier: \"MenuItemTableViewCell\","," cellType: MenuItemTableViewCell.self"," )) { index, item, cell in \/\/ 이 items라는 메소드가 dequeue를 대신 해준다고 보면 된다."," cell.title.text = item.name"," cell.price.text = \"\\(item.price)\""," cell.count.text = \"\\(item.count)\""," }.disposed(by: disposeBag)"],"syntax":"swift"},{"style":"note","name":"Note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이렇게 선언해주면 TableView의 DataSource는 필요없어지게 된다."}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이후에 menuObservable을 "},{"code":"BehaviorSubject","type":"codeVoice"},{"text":"로 바꾸어 주어야 한다.","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/20zu6Rx.png"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이유는 PublishSubject 같은 경우 데이터가 바뀐다면 그때 데이터를 내려보내주기 때문에, 값을 먼저 할당해주고, TableView를 세팅해달라고 bind를 추가해도 값이 이미 할당되어있는 상태이기 때문에 UI가 초기에 설정되지 않는다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 가장 최근에 내려준 데이터를 내보내주는 BehaviorSubject로 바꾸어주어야 한다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"lazy var menuObservable = BehaviorSubject<[Menu]>(value: [])","type":"codeVoice"}]}]}],"type":"unorderedList"}]}]},{"inlineContent":[{"type":"text","text":"그리고 "},{"code":"onOrder","type":"codeVoice"},{"text":" 버튼에 아래와 같은 코드를 추가한다.","type":"text"}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["viewModel.menuObservable.onNext(["," Menu(name: \"changed\", price: 100, count: 2)","])"]},{"type":"paragraph","inlineContent":[{"type":"text","text":"menuObservable에 새로운 Menu 배열을 할당해주고 있다."},{"text":" ","type":"text"},{"text":"앞서 viewModel을 활용해서 UI를 업데이트 해달라는 bind를 해주었기 때문에 값만 수정해주어도 UI는 알아서 업데이트 된다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"text":"count에 2가 아니라 random으로 1…5까지 할당해놓고, 버튼을 계속 누르면 뷰가 계속 업데이트 되는 것을 확인해볼 수 있다.","type":"text"}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/FmBncdh.gif","type":"image"}],"type":"paragraph"},{"type":"thematicBreak"},{"anchor":"MVVM","level":2,"text":"MVVM","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"View","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뷰를 보여주기만 함. 화면을 그리기만 하고 아무 처리 하지않는다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"ViewModel","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"어떤 데이터를 보여줘야하는지, 뷰에 대한 처리를 하는 로직이 들어간다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[상황]"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"기획서가 나왔다. 서버는 서버대로, 디자인은 디자인대로, 클라이언트는 클라이언트 대로 각각 순차적으로 진행하는 것이 아니라 동시에 개발을 진행하는 것이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"뷰는 뷰대로 기획서에 나와있는 모든 내용과 기능들을 Mock 데이터를 활용하여 개발을 하는거다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 뷰에 필요한 모델은 뷰모델로 따로 만드는 것이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"나중에 실제로 서버에서 실제로 데이터가 나오면, 그걸 가지고 실제 모델을 만든다."}]}]},{"content":[{"inlineContent":[{"text":"우리 입장에서는 도메인에서 오는 모델이다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그 모델은 화면에 보여주는 모델하고는 다르다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그래서 도메인에서 오는 모델을 뷰모델로 컨버팅하는 작업이 필요하다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"뷰에서는 뷰에 필요한 뷰모델을 가지고 사용한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[아키텍처로 이야기하는 ViewModel]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"MVC(모델-뷰-컨트롤러) : 보편적으로 많이 사용되는 소프트웨어 디자인 패턴"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Model : 데이터와 명령을 담당","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"View : 화면과 사용자 인터페이스를 담당"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Controller : 모델과 뷰 사이의 명령을 라우팅을 담당","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"MVC의 동작 : View에서 사용자가 입력 -> Controller가 입력을 받아 처리, Model을 변경하고 View가 나타냄"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Controller는 여러 개의 뷰를 가질 수 있음."}]}]}]},{"name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"컨트롤러가 많은 부담을 가지고 있는 패턴, 그리고 View와 Model의 높은 의존성(View가 직접 Model을 보여주므르)","type":"text"}]}],"type":"aside","style":"note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"MVP(모델-뷰-프레젠터) : MVC처럼 모델과 뷰는 동일하나 controller 대신 presenter를 사용하는 패턴"}]}]}]},{"items":[{"content":[{"inlineContent":[{"text":"Presenter: 뷰에서 요청한 정보로 Model을 가공하여 뷰에게 전달","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"UIViewController가 View로 포함 : 입력은 view가 받되 처리는 presenter에게 알려주는 것."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"View와 Model의 의존성을 줄여주므로 소프트웨어가 커졌을 때 유지보수가 용이해진다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"View - Presenter는 1:1 관계이다. 대신 View-Presenter 사이의 의존성이 생긴다는 단점","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"MVVM(모델-뷰-뷰 모델) : MVC처럼 모델과 뷰는 동일하나 view model을 사용하는 패턴"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"view model : view를 표현하기 위해 만든 View를 위한 Model. View를 나타내기 위한 Model이자 View를 나타내기 위한 데이터 처리를 하는 부분"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"화면을 그리는 것은 완전히 view에게 할당하는 것."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"view로부터 입력 action을 받아 model에게 데이터를 요청 및 받은 데이터를 처리, view는 view model과 binding하여 데이터를 보여준다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"view - view model은 1:1 관계, 의존성이 없어 독립성을 띄므로 유지보수 및 관리에 용이한 장점"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[RxCocoa 추가 설명]"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"UI 작업의 특징","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"항상 메인스레드에서 처리해야 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"그래서 "},{"type":"codeVoice","code":".observeOn(MainScheduler.instance)"},{"text":" 코드는 항상 들어가야한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그리고 UI는 데이터를 처리하다가 중간에 에러가 나면 스트림이 끊어져버린다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"끊어진 스트림은 다시 재사용할 수 없다."}]}]},{"content":[{"inlineContent":[{"text":"스트림이 끊어지면 안되니까 아래 코드를 활용한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":".catchErrorJustReturn(\"\")"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"에러가 나면 빈 문자열을 내보내라는 뜻이다."}]}]}]}]},{"content":[{"inlineContent":[{"text":"UI에 대해서 처리하기 위해서는 항상 2줄이 필요하다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/P21BEO2.png"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"이게 번거로우니까 아래와 같은 drive가 나왔다."}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/VSNrbTl.png"}]},{"anchor":"RxRelay","level":2,"text":"RxRelay","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"이렇게 뷰에 대한 스트림은 끊어지면 안되니까 이런것들이 나왔는데, subject도 끊어지지 않는 subject가 나왔다."}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Subject랑 동일하지만 에러가 나도 스트림이 끊어지지 않는다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"에러가 나지 않으니 받아들일 수 밖에 없다. 그래서 데이터를 전달할 때 onNext로 전달하는 것이 아니라 accept로 전달한다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"style":"note","type":"aside","content":[{"inlineContent":[{"type":"text","text":"Observable의 UI 전용으로 "},{"type":"codeVoice","code":"Driver"},{"text":" ","type":"text"},{"type":"text","text":"Subject의 UI 전용으로 "},{"type":"codeVoice","code":"Relay"},{"type":"text","text":" "},{"type":"image","identifier":"https:\/\/i.imgur.com\/ccabuVQ.png"}],"type":"paragraph"}],"name":"Note"},{"type":"aside","name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"스레드에 지연을 주는 방법","type":"text"},{"text":" ","type":"text"},{"type":"image","identifier":"https:\/\/i.imgur.com\/ovgQ8LK.png"},{"text":" ","type":"text"},{"code":".delay","type":"codeVoice"},{"type":"text","text":"를 주면 된다~"}]}],"style":"note"},{"name":"Note","content":[{"inlineContent":[{"type":"text","text":"3초 후에 딱 1번만 얼럿을 보여주게 하기"},{"type":"text","text":" "},{"type":"image","identifier":"https:\/\/i.imgur.com\/qH0646Z.png"}],"type":"paragraph"}],"type":"aside","style":"note"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/www.youtube.com\/watch?v=iHKBNYMWd5I"}]}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"references":{"https://i.imgur.com/6K4NidU.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/6K4NidU.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/6K4NidU.png","traits":["1x","light"]}]},"https://i.imgur.com/STrQA41.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/STrQA41.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/STrQA41.png","traits":["1x","light"]}]},"https://i.imgur.com/20zu6Rx.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/20zu6Rx.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/20zu6Rx.png","traits":["1x","light"]}]},"https://i.imgur.com/ezu4nj3.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/ezu4nj3.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/ezu4nj3.png","traits":["1x","light"]}]},"https://i.imgur.com/qH0646Z.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/qH0646Z.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/qH0646Z.png","traits":["1x","light"]}]},"https://i.imgur.com/brz1tWB.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/brz1tWB.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/brz1tWB.png","traits":["1x","light"]}]},"https://i.imgur.com/BYS4xQI.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/BYS4xQI.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/BYS4xQI.png","traits":["1x","light"]}]},"https://i.imgur.com/N3aZ9Pa.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/N3aZ9Pa.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/N3aZ9Pa.png","traits":["1x","light"]}]},"https://i.imgur.com/QlqeOOb.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/QlqeOOb.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/QlqeOOb.png","traits":["1x","light"]}]},"https://i.imgur.com/iFteEbO.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/iFteEbO.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/iFteEbO.png","traits":["1x","light"]}]},"https://i.imgur.com/VwlIpHE.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/VwlIpHE.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/VwlIpHE.png","traits":["1x","light"]}]},"https://i.imgur.com/aBIS3B0.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/aBIS3B0.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/aBIS3B0.png","traits":["1x","light"]}]},"https://i.imgur.com/VSNrbTl.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/VSNrbTl.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/VSNrbTl.png","traits":["1x","light"]}]},"https://i.imgur.com/u7M2N7r.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/u7M2N7r.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/u7M2N7r.png","traits":["1x","light"]}]},"https://i.imgur.com/FmBncdh.gif":{"type":"image","identifier":"https:\/\/i.imgur.com\/FmBncdh.gif","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/FmBncdh.gif","traits":["1x","light"]}]},"https://i.imgur.com/DAe1iiG.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/DAe1iiG.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/DAe1iiG.png","traits":["1x","light"]}]},"https://i.imgur.com/sjYe0mD.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/sjYe0mD.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/sjYe0mD.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote/Rx":{"type":"topic","abstract":[{"type":"text","text":"RxSwift를 활용한 비동기 프로그래밍"}],"role":"collectionGroup","url":"\/documentation\/arinote\/rx","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Rx","title":"RxSwift","kind":"article"},"https://i.imgur.com/ccabuVQ.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/ccabuVQ.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/ccabuVQ.png","traits":["1x","light"]}]},"https://i.imgur.com/bDymOxA.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/bDymOxA.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/bDymOxA.png","traits":["1x","light"]}]},"https://i.imgur.com/fFQsQNo.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/fFQsQNo.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/fFQsQNo.png","traits":["1x","light"]}]},"https://i.imgur.com/xMyfsQH.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/xMyfsQH.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/xMyfsQH.png","traits":["1x","light"]}]},"https://i.imgur.com/IUJfjh1.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/IUJfjh1.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/IUJfjh1.png","traits":["1x","light"]}]},"https://i.imgur.com/ovgQ8LK.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/ovgQ8LK.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/ovgQ8LK.png","traits":["1x","light"]}]},"https://i.imgur.com/BHsYbEV.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/BHsYbEV.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/BHsYbEV.png","traits":["1x","light"]}]},"https://i.imgur.com/LLsMZXh.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/LLsMZXh.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/LLsMZXh.png","traits":["1x","light"]}]},"https://i.imgur.com/l8yu9Ur.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/l8yu9Ur.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/l8yu9Ur.png","traits":["1x","light"]}]},"https://i.imgur.com/lXZIWVC.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/lXZIWVC.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/lXZIWVC.png","traits":["1x","light"]}]},"https://i.imgur.com/Pts6L5X.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/Pts6L5X.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/Pts6L5X.png","traits":["1x","light"]}]},"https://i.imgur.com/JY8cyD4.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/JY8cyD4.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/JY8cyD4.png","traits":["1x","light"]}]},"https://i.imgur.com/5UXT0A0.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/5UXT0A0.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/5UXT0A0.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/P21BEO2.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/P21BEO2.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/P21BEO2.png","traits":["1x","light"]}]},"https://i.imgur.com/vL8l4CS.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/vL8l4CS.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/vL8l4CS.png","traits":["1x","light"]}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220221_localization,_--_----,_swiftui_tutorial.json b/docs/data/documentation/arinote/220221_localization,_--_----,_swiftui_tutorial.json new file mode 100644 index 0000000..4150bd5 --- /dev/null +++ b/docs/data/documentation/arinote/220221_localization,_--_----,_swiftui_tutorial.json @@ -0,0 +1 @@ +{"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220221_localization,_--_----,_swiftui_tutorial"]}],"metadata":{"title":"220221 Localization, 퀴즈 오답노트, SwiftUI Tutorial","roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}]},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220221_Localization,_--_----,_SwiftUI_Tutorial","interfaceLanguage":"swift"},"sections":[],"primaryContentSections":[{"content":[{"level":1,"text":"TIL (Today I Learned)","type":"heading","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"text":"2월 21일 (월)","type":"text"}]},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"type":"heading","text":"학습 내용"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"활동학습 퀴즈 오답노트"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Localization 활동학습"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SwiftUI - Stanford Tutorial"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"heading","text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[퀴즈 오답노트]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"CoreData는 "},{"type":"codeVoice","code":"Foundation"},{"type":"text","text":"을 참조할까?"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/WvELlD9.png"}]}]},{"content":[{"inlineContent":[{"text":"참조하고 있다!","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"height intrinsic size","type":"codeVoice"},{"type":"text","text":"을 가지는 것은?"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIButton"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"width, height 모두 intrinsic size가 존재한다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"UISlider"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"width는 없고 height는 존재한다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UITextField"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"width, height 모두 존재한다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UITextView"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"내용이 있든 없든 ","type":"text"},{"code":"isScrollEnabled == true","type":"codeVoice"},{"type":"text","text":" 라면 width, height 모두 intrinsic size가 없다."}]}]},{"content":[{"inlineContent":[{"text":"false인 경우에는 내용이 있을 때 스크롤을 끄거나, 내용이 없을 때 스크롤을 꺼도 intrinsic size가 생긴다.","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로젝트에 적용된 코코아팟과 라이브러리 버전을 확인할 수 있는 파일은?"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Podfile.lock"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"pod install을 하고나면 Podfile 이외에 Podfile.lock이라는 파일이 같이 생긴다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Podfile.lock은 pod의 버전을 계속 추적하여 기록해놓고 유지시키는 역할을 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"또한 Podfile.lock에는 유일성을 보증하는 해쉬값인 CHECKSUM이 부여된다."}]}]},{"content":[{"inlineContent":[{"text":"만약 pod 버전에 하나라도 변화가 생긴다면 CHECKSUM 또한 변하게 되는 것이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이경우 Git을 사용할 경우 Podfile.lock이 diff로 잡히게 된다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Data Container 중 Library는 사용자가 접근할 수 있는 영역일까?"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"아니다. Library는 사용자에게 직접 노출되지 않고, 앱의 기능이나 관리에 필요한 파일을 저장해두는 파일이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"사용자에게 노출되면 안되는 파일을 여기에 저장한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Data Container 중 사용자가 유일하게 접근할 수 있는 영역은 "},{"code":"Documents","type":"codeVoice"},{"type":"text","text":"이다."}]}]}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Localization]"}]}]},{"type":"heading","text":"지역화란?","anchor":"%EC%A7%80%EC%97%AD%ED%99%94%EB%9E%80","level":3},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"지역화는 현지화한다는 뜻을 가졌다"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"즉, 해당 언어와 나라 지역에 맞게 앱을 설정해주는 것을 뜻한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"국제화(internationalization)를 I18N or i18n으로, 지역화(localization)를 L10N이나 l10n으로 표기한다","type":"text"}]}]}],"type":"unorderedList"},{"anchor":"%EC%A7%80%EC%97%AD%ED%99%94%EC%9D%98-%EC%A0%84%EC%A0%9C%EC%A1%B0%EA%B1%B4","type":"heading","level":3,"text":"지역화의 전제조건"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 앱이 지역화가 되려면 여러 국가에 배포되어 국제화 되어있는 앱이라는 조건이 있어야 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 앱이 한국에서만 사용되는 앱이라면 지역화가 의미 없을 것이다."}]}]}]},{"anchor":"%EC%A7%80%EC%97%AD%ED%99%94-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%9A%94%EC%86%8C","text":"지역화 가능한 요소","level":3,"type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"RTL, LTR (문화권에 따른 읽기\/쓰기 방식), 언어, 시간, 날짜, 주소, 화폐단위 및 통화, 이미지 등등…","type":"text"}],"type":"paragraph"}]}]},{"anchor":"%EC%A7%80%EC%97%AD%ED%99%94%EC%99%80-%EC%A0%91%EA%B7%BC%EC%84%B1%EC%9D%98-%EA%B4%80%EA%B3%84","text":"지역화와 접근성의 관계","level":3,"type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"지역화를 함으로 여러 국가와 지역에서 해당 앱에 대한 접근성(accessibility)가 우수해진다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"접근성은 애플의 가장 강점인 부분으로 꼭 이 부분을 잘 활용하여 구현해놓으면 좋다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"접근성(accessibility)을 설정하려면 accessibility inspector를 활용하여 여러가지를 구현할 수 있다."}]}]}],"type":"unorderedList"}]}]},{"inlineContent":[{"text":"언어 지역화","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"지역화 하려는 언어를 프로젝트에 추가한다."}]},{"items":[{"content":[{"inlineContent":[{"text":"타겟을 선택해서 다국어화","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/LXGcj3d.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"코드로 다국어 처리","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Strings 파일을 생성하고","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"Localizable.strings","type":"codeVoice"},{"text":" 로 네이밍 변경","type":"text"}]}]}]}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/WoU9xAR.png"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Localize… 버튼 클릭"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/y32fgtE.png"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"다시 타겟으로 돌아가서 지역화하고 싶은 언어를 추가해주기"}]}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/vbkbUyR.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"아까 만든 파일을 체크해주고 Finish","type":"text"}],"type":"paragraph"}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/nHotMZL.png"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로젝트에 파일이 생성되어 있는 모습"}]}]}],"type":"unorderedList"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/jcCIwcf.png","type":"image"}],"type":"paragraph"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/yclkqR1.png"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Localizable.strings에 다국어 처리를 햅주면 되는데, Key와 Value로 다국어 처리를 해줄 수 있다."}]}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/6bOA2a5.png","type":"image"}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/ne1UxDS.png","type":"image"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"그리고 다국어화 한 문자열을 사용할 땐 "},{"type":"codeVoice","code":"NSLocalizedString"},{"type":"text","text":" 메소드를 활용해주어야 하는데, 번거로우니 extension을 활용하여 간단히 사용해볼 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["test.text = String(format: NSLocalizedString(\"Test\", comment: \"\"))","","\/\/ String Extension for Localization","extension String {"," var localized: String {"," \treturn NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: \"\", comment: \"\")"," }","}","text.text = \"Test\".localized"]},{"name":"Note","type":"aside","style":"note","content":[{"inlineContent":[{"type":"text","text":"스토리보드를 코드로 말고 Interface Builder Storyboard 옵션을 활용하여 스토리보드 자체를 지역화해줄 수도 있다."}],"type":"paragraph"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/x8neP9B.png"}]},{"name":"Note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱의 언어를 바꿀 때는 App Language, App Region 둘다 바꿔주자."},{"text":" ","type":"text"},{"identifier":"https:\/\/i.imgur.com\/sR5vqSC.png","type":"image"}]}],"style":"note"},{"type":"aside","style":"note","name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"이미지의 지역화는 Assets에 접근해서 이미지를 클릭후 우측 인스펙터에서 Localization을 활성화 시켜주면 된다.","type":"text"},{"text":" ","type":"text"},{"identifier":"https:\/\/i.imgur.com\/xCXc1AU.png","type":"image"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"날짜 지역화"}]}],"type":"aside","style":"note","name":"Note"},{"type":"codeListing","syntax":"swift","code":["let date = DateFormatter.localizedString(from: Date(), dateStyle: .medium, timeStyle: .short)"," dateTimeLabel.text = date"]},{"name":"Note","content":[{"inlineContent":[{"type":"text","text":"통화 지역화"}],"type":"paragraph"}],"style":"note","type":"aside"},{"syntax":"swift","code":["func currency(text: Double) -> String? {"," let locale = Locale.current"," let price = text as NSNumber"," let formatter = NumberFormatter()",""," formatter.numberStyle = .currency"," formatter.currencyCode = locale.languageCode"," formatter.locale = locale",""," return formatter.string(from: price)","}","","currencyLabel.text = currency(text: 3000.34)"],"type":"codeListing"},{"name":"Note","type":"aside","style":"note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뷰의 방향을 지역화 (방향 바꿀 때에도 유용하게 쓰는 듯?)"}]}]},{"syntax":"swift","code":["view.semanticContentAttribute = .forceRightToLeft"],"type":"codeListing"},{"name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"여러 문자열들을 지역화할 때 구글 스프레드 시트를 활용하기","type":"text"}]}],"style":"note","type":"aside"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/U4VhRNW.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"구글 스프레드 시트를 새로 생성한 후 위 사진과 같이 국가코드와 번역할 문장을 적으면 된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"좌측에 국제코드를 적고 우측에 아래 코드를 적으면 번역한 문장이 생성된다. (","type":"text"},{"type":"reference","identifier":"https:\/\/www.ibabbleon.com\/iOS-Language-Codes-ISO-639.html","isActive":true},{"type":"text","text":")"}]}]}]},{"syntax":null,"type":"codeListing","code":["\/\/ 예시","=GOOGLETRANSLATE(\"Welcome to yagom-academy\", \"en\", A10)"]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[SwiftUI - Stanford Tutorial]","type":"text"}]}]},{"text":"Getting started with SwiftUI","type":"heading","level":3,"anchor":"Getting-started-with-SwiftUI"},{"type":"aside","style":"note","content":[{"type":"paragraph","inlineContent":[{"text":"프로젝트를 생성한다.","type":"text"},{"text":" ","type":"text"},{"type":"image","identifier":"https:\/\/i.imgur.com\/IjqHctr.png"}]}],"name":"Note"},{"type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그럼 다음과 같은 코드가 기본적으로 생성된다"}]}],"name":"Note","style":"note"},{"type":"codeListing","syntax":"swift","code":["import SwiftUI","","@main","struct MemorizeApp: App {"," var body: some Scene {"," WindowGroup {"," ContentView()"," }"," }","}"]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"먼저 MemorizeApp.swift 파일을 살펴보면, App 프로토콜을 채택하고 있는 것을 볼 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"SwiftUI App Life Cycle을 사용하는 앱은 App 프로토콜을 준수해야하고, body 프로퍼티는 하나 이상의 scene을 반환한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"@main은 앱의 진입점을 가리키는 attribute identifier이다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["import SwiftUI","","struct ContentView: View {"," var body: some View { \/\/ UI를 구성하는 코드"," Text(\"Hello, world!\")"," .padding()"," }","}","","struct ContentView_Previews: PreviewProvider {"," static var previews: some View { \/\/ 우측에 Preview를 띄워주는 코드"," ContentView()"," }","}"]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기본적으로 SwiftUI View file은 두 가지 구조체를 선언한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"첫번째 구조체는 View 프로토콜을 준수하며 뷰의 컨텐츠와 레이아웃을 묘사한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"두 번째 구조체는 해당 뷰에 대한 Preview를 선언하고 있다."}]}]}],"type":"unorderedList"},{"type":"aside","style":"note","name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"Text의 속성을 바꾸고 싶다면?","type":"text"}]}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"command 키를 누르고 Text를 클릭하면 아래와 같은 창이 뜨는데, Show SwiftUI Inspector…를 눌러주자","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/RRBIzHm.png"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그러면 Text의 각 속성값들을 바꿀 수 있는 Inspector가 표시되는 것을 확인할 수 있다."}]}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/xCloFxQ.png"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"혹은 control + option 키를 같이 누르고 Text를 클릭해도 표시된다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/LXRgwOu.png"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"인스펙터에서 설정한 모든 것들이 코드로 바로바로 표기되는 것을 확인해볼 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SwiftUI View를 커스터마이징 하기 위해서는 modifiers라는 메서드를 호출해야한다. 이 modifiers는 뷰를 랩핑하여 디스플레이나 다른 속성들을 변경한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"각각의 modifiers는 새로운 View를 반환하므로 여러 modifier를 수직으로 쌓듯이 연결하는 것이 일반적이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 SwiftUI Inspector를 통해 변경하던지, 아니면 코드의 modifer를 수정하던지 이러한 변경사항들을 Xcode는 즉각 업데이트 한다.","type":"text"}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Combine Views Using Stacks]"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"SwiftUI 뷰를 생성할 때 뷰의 body 프로퍼티에는 content, layout 및 동작을 기술한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하지만 이 body 프로퍼티는 오직 하나의 싱글 뷰만 반환한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하지만 스택을 활용한다면 이 뷰들을 결합시키고 임베드할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스택에는 총 3가지 종류가 있다"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"HStack","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Horizontal Stack 수평 스택"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"VStack","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Vertical Stack 수직 스택"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ZStack"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Z축을 기준으로 뷰를 쌓는 스택"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"ZStack안에 뷰를 여러개 넣는다고 가정하면 제일 상위뷰에 addsubview하는 느낌이라고 생각하면 된다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]}],"type":"unorderedList"},{"type":"aside","style":"note","name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"Custom View를 생성","type":"text"}]}]},{"code":["struct CardView: View {"," var body: some View {"," ZStack {"," RoundedRectangle(cornerRadius: 25.0)"," .foregroundColor(.white)"," RoundedRectangle(cornerRadius: 25.0)"," .stroke(lineWidth: 3)"," Text(\"🍎\")"," .foregroundColor(.orange)"," .font(.largeTitle)"," }"," }","}"],"type":"codeListing","syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"ZStack으로 쌓여있다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"RoundedRectangle가 2개","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Text가 1개"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"총 3개가 쌓여있다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이렇게 만들어진 뷰를 ContentView에 HStack으로 쌓아올려보자","type":"text"}]}]}]},{"type":"codeListing","syntax":"swift","code":["struct ContentView: View {"," var body: some View {"," HStack {"," CardView()"," CardView()"," CardView()"," CardView()"," }"," .padding()"," .foregroundColor(.red)"," }","}"]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이런식으로 View를 생성하여 레고처럼 쌓을 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"content":[{"inlineContent":[{"text":"Preview 디바이스에 다크모드 적용방법","type":"text"}],"type":"paragraph"}],"name":"Note","type":"aside","style":"note"},{"type":"codeListing","syntax":"swift","code":["struct ContentView_Previews: PreviewProvider {"," static var previews: some View {"," ContentView()"," .preferredColorScheme(.dark) \/\/ 이 부분을 추가해주면 다크모드로 변한다."," }","}"]},{"type":"codeListing","code":["struct ContentView_Previews: PreviewProvider {"," static var previews: some View {"," ContentView()"," .preferredColorScheme(.dark)"," ContentView()"," .preferredColorScheme(.light)"," }","}"],"syntax":"swift"},{"items":[{"content":[{"inlineContent":[{"text":"이렇게 한다면 라이트모드, 다크모드 두개의 디바이스를 Preview로 확인해볼 수 있다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[@State]"}]}]},{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/swiftui\/state"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"View 구조체 안에서 var 키워드로 선언한 프로퍼티의 값을 구조체 내에서 변경하려고 하면 에러가 뜬다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Swift 구조체에서 mutating으로 선언되지 않은 연산 프로퍼티는 구조체 내부에서 그 값의 변경이 불가능하다."}]}]},{"content":[{"inlineContent":[{"text":"이럴 때 ","type":"text"},{"type":"codeVoice","code":"@State"},{"text":" 라는 속성을 부여해주면 해결해줄 수 있는데, 어떤 역할을 하는 것일까?","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"단어 그대로 현재 상태를 나타내는 속성으로써 뷰의 어떤 값을 저장하는 데 사용한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"즉, State는 value 자체가 아니다. 값을 읽고 변경하는 수단으로, state의 기본값에 접근하려면 value프로퍼티를 사용해야 한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"그리고 현재 UI의 특정 상태를 저장하기 위해 만들어진 것이기 때문에 보통 private으로 지정하여 사용한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"State 값이 변경되면 View가 appearance를 inbalidates하고 body를 다시 계산(recomputes)한다","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"codeListing","code":["struct CardView: View {"," @State private var isFaceUp: Bool = false \/\/ @State 속성 부여"," "," var body: some View {"," ZStack {"," let shape = RoundedRectangle(cornerRadius: 25.0)"," if isFaceUp { \/\/ 속성에 따라 카드를 다르게 보여주기"," shape"," .foregroundColor(.white)"," shape"," .stroke(lineWidth: 3)"," Text(\"🍎\")"," .foregroundColor(.orange)"," .font(.largeTitle)"," } else {"," shape"," .fill()"," }"," }"," .onTapGesture {"," self.isFaceUp = !isFaceUp \/\/ 터치할 때마다 프로퍼티 값을 바꿔준다."," }"," }","}"],"syntax":"swift"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"터치 이벤트가 발생할 때 마다 CardView 구조체가 갖고 있는 isFaceUp 변수에 있는 값을 변경하도록 코드를 수정해주었다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"aside","content":[{"level":3,"type":"heading","anchor":"onTapGesturecountperform","text":"onTapGesture(count:perform:)"}],"style":"note","name":"Note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"SwiftUI에서 관리하는 값을 읽고 쓸 때 사용하는 메소드이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"상태값이 변경되면 View를 다시 계산하고 업데이트 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"인스턴스 값이 아니라 값을 읽고 쓰는 수단이다","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"주의할 점은 뷰의 바디 안에서 또는 뷰에서 호출한 메소드에서만 접근해야하므로 뷰의 클라이언트가 접근하지 못하도록 private하게 선언해야 한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"사용할 때는 $와 함께 사용하면 된다.","type":"text"}]}]}]},{"name":"Note","type":"aside","content":[{"anchor":"%EC%9D%BC%EB%B0%98-%EB%B3%80%EC%88%98%EB%A1%9C-%EC%84%A0%EC%96%B8%ED%95%B4%EC%84%9C-%EC%93%B0%EB%A9%B4-%EB%90%98%EB%8A%94-%EA%B2%83%EC%9D%B4-%EC%95%84%EB%8B%8C%EA%B0%80","text":"일반 변수로 선언해서 쓰면 되는 것이 아닌가?","type":"heading","level":3}],"style":"note"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"그냥 변수로 선언할 경우 Cannot use mutating member on immutable value: ‘self’ is immutable 라는 에러가 발생한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Swift는 mutating으로 선언되지 않은 연산 프로퍼티 구조체 내부에서 값 변경이 불가능하다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"name":"Note","style":"note","content":[{"type":"heading","text":"mutating으로 선언하면 되면 안될까…?","anchor":"mutating%EC%9C%BC%EB%A1%9C-%EC%84%A0%EC%96%B8%ED%95%98%EB%A9%B4-%EB%90%98%EB%A9%B4-%EC%95%88%EB%90%A0%EA%B9%8C","level":3}],"type":"aside"},{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/swiftui\/view"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"여기서 살펴볼 것은 self는 View 프로토콜을 채택한 뷰이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"View의 내부를 살펴보면 body가 get으로 되어있는 것을 확인할 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉 mutating으로 바꿀 수가 없다."}]}]},{"content":[{"inlineContent":[{"text":"고로 상태를 변경하기 위해선 @State를 이용해야한다.","type":"text"}],"type":"paragraph"}]}]},{"name":"Note","type":"aside","style":"note","content":[{"level":3,"anchor":"$%EB%8A%94-%EC%96%B8%EC%A0%9C-%EC%93%B0%EB%8A%94%EA%B1%B0%EC%A7%80","text":"$는 언제 쓰는거지?","type":"heading"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"$는 Binding을 파라미터로 가지고 있는 View들과 바인딩 하여 사용할 수 있다."}]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[ForEach]"}],"type":"strong"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/swiftui\/foreach"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"일반적으로 SwiftUI에서 Sequence를 반복하여 뷰들을 생성하고자 하는 경우 ForEach를 활용한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"ForEach는 자체적인 뷰 구조체이며, 원하는 경우 직접적으로 뷰 body로 반환할 수 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"항목의 배열을 제공하고 값이 변경될 때 업데이트 하는 방법을 알기 위해서 각각의 고유하게 식별할 수 있는 방법을 SwiftUI에게 알려줘야할 수도 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"또한 반복문의 각 항목에 대한 뷰를 생성하기 위해 실행할 클로저를 전달한다.","type":"text"}]}]}]},{"style":"note","name":"Note","type":"aside","content":[{"text":"Id: .self","anchor":"id-self","level":3,"type":"heading"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"SwiftUI가 배열의 각 요소를 고유하게 식별할 수 있도록 하기 위해 필요한 파라미터다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"항목을 추가하거나 삭제 시 어떤 것을 처리했는지 SwiftUI가 정확히 알고 있다는 뜻이다.","type":"text"}],"type":"paragraph"}]}]},{"style":"note","name":"Note","content":[{"type":"heading","level":3,"text":"배열에 Custom 타입이 있는 경우에는???","anchor":"%EB%B0%B0%EC%97%B4%EC%97%90-Custom-%ED%83%80%EC%9E%85%EC%9D%B4-%EC%9E%88%EB%8A%94-%EA%B2%BD%EC%9A%B0%EC%97%90%EB%8A%94"}],"type":"aside"},{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/swiftui\/view\/id(_:)"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"타입에서 고유하게 식별할 수 있는 프로퍼티인 id를 사용해야한다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"고유함을 보장하는 의미의 UUID 타입을 가진 프로퍼티를 만들어 활용할 수 있다.","type":"text"}],"type":"paragraph"}]}]}]}]},{"type":"codeListing","code":["struct SimpleGameResult {"," let id = UUID()"," let score: Int","}","","struct ContentView: View {"," let results = ["," SimpleGameResult(score: 8),"," SimpleGameResult(score: 5),"," SimpleGameResult(score: 10)"," ]",""," var body: some View {"," VStack {"," ForEach(results, id: \\.id) { result in"," Text(\"Result: \\(result.score)\")"," }"," }"," }","}"],"syntax":"swift"},{"content":[{"text":"Protocol `identifier`","level":3,"anchor":"protocol-identifier","type":"heading"}],"style":"note","type":"aside","name":"Note"},{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/swift\/identifiable","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"이 프로토콜을 준수하는 타입을 만들었을 경우에는 id: .self를 제외한 ForEach를 사용할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"해당 프로토콜을 준수한다는 것은 각 인스턴스를 고유하게 식별하는 id 프로퍼티를 추가한다는 것을 의미한다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"syntax":"swift","code":["struct IdentifiableGameResult: Identifiable {"," var id = UUID()"," var score: Int","}","","struct ContentView: View {"," let results = ["," IdentifiableGameResult(score: 8),"," IdentifiableGameResult(score: 5),"," IdentifiableGameResult(score: 10)"," ]",""," var body: some View {"," VStack {"," ForEach(results) { result in"," Text(\"Result: \\(result.score)\")"," }"," }"," }","}"],"type":"codeListing"},{"style":"note","type":"aside","name":"Note","content":[{"inlineContent":[{"type":"text","text":"ContentView에 배열을 추가하여 ForEach를 사용한 예제"}],"type":"paragraph"}]},{"type":"codeListing","code":["struct ContentView: View {"," var emojis = [\"🍎\", \"🍐\", \"🍑\", \"🍒\"]"," var body: some View {"," HStack {"," ForEach(emojis, id: \\.self) { emoji in"," CardView(contant: emoji)"," }"," }"," .padding()"," .foregroundColor(.red)"," }","}"],"syntax":"swift"},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[Button]"}],"type":"strong"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/swiftui\/button","type":"text"}]},{"code":["Button(action: {"," \/\/ action","}) {"," \/\/ display","}"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"inlineContent":[{"text":"SwiftUI에서 일반 버튼을 만들기 위해서는 위와 같이 선언해주면 된다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"action은 사용자가 버튼을 클릭하거나 탭할 때 작업을 수행하는 클로저 프로퍼티이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Label은 버튼의 텍스트나 아이콘 같은 동작을 실행하는 View를 나타낸다."}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"syntax":"swift","code":["\/\/ 카드를 추가하는 버튼을 예시로 만들어보자."," var add: some View { \/\/ 버튼 프로퍼티 생성"," return Button(action: { \/\/ 버튼을 클릭했을 때 실행할 액션"," if emojiCount < emojis.count {"," emojiCount += 1"," }"," }, label: {"," HStack { \/\/ 버튼의 외관"," Image(systemName: \"plus.circle\")"," }"," })"," }"],"type":"codeListing"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[Spacer]","type":"text"}]}],"type":"paragraph"},{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/swiftui\/spacer","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이름에서부터 알 수 있 듯 어떤 빈 공간을 만들어준다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이 빈 공간은 다른 뷰의 크기에 Priority를 두고 그 크기가 변하지 않는 선에서 본인의 크기를 최대한 늘리고자하는 성질을 가지고 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 파라미터 minLength를 주지 않고 Spacer()를 그냥 호출할 경우, 늘어날 수 있는 최대한의 크기만큼 여백이 생긴다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/VXbEder.png","type":"image"}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["\/\/ 여기까지 Full Code","struct ContentView: View {"," var emojis = [\"🍎\", \"🍐\", \"🍑\", \"🍒\", \"🍊\", \"🥝\", \"🍌\", \"🍈\", \"🍓\", \"🥭\", \"🍍\", \"🍅\", \"🍇\", \"🍋\", \"🫐\"]"," @State var emojiCount = 4"," var body: some View {"," VStack {"," HStack {"," \/\/ for문을 emojiCount만큼 반복하기"," ForEach(emojis[0.. 1 {"," emojiCount -= 1"," }"," }, label: {"," HStack {"," Image(systemName: \"minus.circle\")"," }"," })"," }","}"]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[LazyVGrid]"}],"type":"strong"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/swiftui\/lazyvgrid"}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/4gNdPfi.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Grid는 List와 친척이라고 생각하면 된다"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Grid는 horizontal Direction으로 사진을 배열하여 화면을 구성하기 좋다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"가장 좋은 예로 Instargram, Netflix의 레이아웃을 떠올리면 된다.","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/rlweSrn.jpg","type":"image"}]},{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/swiftui\/lazyvgrid","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"columns 파라미터를 통해 그리드를 깔아줄 열의 개수만큼 GridItem() 배열을 넘겨주면 열의 개수를 지정할 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"특정 열에 fixed()를 활용해서 고정값을 주어 각 열의 크기를 다르게 조절할 수도 있다."}]}]}],"type":"unorderedList"},{"type":"aside","content":[{"level":3,"anchor":"Lazy-%ED%95%98%EB%8B%A4%EB%8A%94-%EC%9D%98%EB%AF%B8%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C","type":"heading","text":"Lazy 하다는 의미는 무엇일까?"}],"style":"note","name":"Note"},{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/swiftui\/creating-performant-scrollable-stacks"}]},{"content":[{"text":"Creating Performant Scrollable Stacks","type":"heading","level":3,"anchor":"Creating-Performant-Scrollable-Stacks"}],"type":"aside","style":"note","name":"Note"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Display large numbers of repeated views efficiently with scroll views, stack views, and lazy stacks."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"SwfitUI에서는 ","type":"text"},{"type":"codeVoice","code":"LazyVStack"},{"text":" , ","type":"text"},{"code":"LazyHStack","type":"codeVoice"},{"text":" , ","type":"text"},{"code":"LazyVGrid","type":"codeVoice"},{"type":"text","text":" , "},{"type":"codeVoice","code":"LazyHGrid"},{"type":"text","text":" 와 같이 Lazy 라는 이름이 붙은 뷰 빌더들을 볼 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"lazy와 함께 선언된 프로퍼티는 처음 호출될 때 값을 계산하기 때문에 필요하지 않는 시간 동안은 값을 생성하지 않는다."}]}]},{"content":[{"inlineContent":[{"text":"따라서 SwiftUI에서 Lazy라는 이름이 앞에 붙은 뷰 빌더들도 화면에 그려야할 필요가 있는 뷰들에 한해서만 body를 계산한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"때문에 ","type":"text"},{"type":"codeVoice","code":"화면에 굳이 나타낼 필요가 없는 데이터"},{"text":"들을 계산하지 않기 때문에 많은 데이터를 다루는 경우 ","type":"text"},{"type":"codeVoice","code":"효율적"},{"type":"text","text":"으로 사용될 수 있다."}],"type":"paragraph"}]}]},{"style":"note","name":"Note","type":"aside","content":[{"inlineContent":[{"text":"Stack views and lazy stacks have similar functionality, and they may feel interchangeable, but they each have strengths in different situations. Stack views load their child views all at once, making layout fast and reliable, because the system knows the size and shape of every subview as it loads them. Lazy stacks trade some degree of layout correctness for performance, because the system only calculates the geometry for subviews as they become visible.","type":"text"},{"type":"text","text":" "},{"type":"text","text":"When choosing the type of stack view to use, always start with a standard stack view and only switch to a lazy stack if profiling your code shows a worthwhile performance improvement."}],"type":"paragraph"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"공식문서에서도 처음에는 표준 Stack을 통해 구현하면서 Profiling을 통해 성능을 확인해보다가 lazy Stack을 활용하였을 때 ","type":"text"},{"type":"codeVoice","code":"유의미한 성능 개선이 발생된다면 사용하라고 권장"},{"type":"text","text":"하고 있다."}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[GridItem]"}]}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/swiftui\/griditem"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"GridItem 인스턴스들은 LazyVGrid, LazyHGrid 뷰에서 그릴 아이템들의 레이아웃(간격, 정렬, 사이즈 등)을 설정하는 데에 사용된다."}]}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":[".init(GridItem.Size, spacing: CGFloat?, alignment: Alignment?)"]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Size에는 총 3가지 타입이 있다"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"adaprive"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"minimum 값 이상의 사이즈로 열마다 가능한 많은 아이템들을 배치하고자 할 때 사용되는 사이즈"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"flexible"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"minimum 값 이상의 사이즈로 column 수를 조절하고 싶을 때 사용되는 사이즈","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"adaptive와 유사하나 열마다 배치되는 아이템 수를 조절할 수 있다는 점에서 다르다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"fixed","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"column 수와 크기를 직접 조절하고 싶을 때 사용하는 사이즈"}],"type":"paragraph"}]}]}]}]}]}]},{"syntax":"swift","code":["\/\/ 위 Full Code에 ForEach를 LazyVGrid로 감싸서 그리드뷰를 구성","LazyVGrid(columns: [GridItem(.adaptive(minimum: 80))]) { \/\/ 화면을 그리드 형식으로 최소사이즈 80"," \/\/ for문을 emojiCount만큼 반복하기"," ForEach(emojis[0.. [Note]? {"," let context = persistentContainer.viewContext"," let request = NSFetchRequest(entityName: entityName)"," request.predicate = predicate"," request.returnsObjectsAsFaults = false"," request.sortDescriptors = sortDescriptors"," guard let newData = try? context.fetch(request) as? [Note] else {"," return nil"," }"," return newData"," }","...","","func setUpNotes() { \/\/ fetch를 활용하여 notes를 설정하는 메소드"," guard let newData = fetch() else {"," return"," }"," self.notes = newData"," }","..."]},{"type":"paragraph","inlineContent":[{"type":"text","text":"내 경우에는 코어데이터를 관리하는 타입 내부에서 tableView에 보여질 데이터도 같이 관리하기 때문에, fetch를 받아온 다음 배열에 할당해주는 기능을 추가하여 진행해주었다."}]},{"type":"codeListing","syntax":"swift","code":["final class PersistentManager {","private(set) var notes = [Note]()","..."," func searchNote(text: String) {"," guard let searchData = fetch("," predicate: NSPredicate(format: \"body CONTAINS[c] %@\", text) \/\/ body에 text가 포함되어있는 것들을 필터링"," ) else {"," return"," }"," self.notes = searchData"," }","..."]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"따라서 위와 같은 "},{"code":"searchNote(text:)","type":"codeVoice"},{"type":"text","text":" 라는 메소드를 구현해주었다."}],"type":"paragraph"}]}]},{"syntax":"swift","type":"codeListing","code":["extension NotesViewController: UISearchResultsUpdating {"," func updateSearchResults(for searchController: UISearchController) {"," guard let searchText = searchController.searchBar.text, searchText != \"\" else { \/\/ 검색어가 비어있다면 return"," return"," }"," PersistentManager.shared.searchNote(text: searchText)"," tableView.reloadData()"," }","}"]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 "},{"type":"codeVoice","code":"UISearchResultsUpdating"},{"type":"text","text":"를 채택하여 "},{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uisearchresultsupdating\/1618658-updatesearchresults"},{"text":" 메소드를 구현하여 위에서 만들었던 searchNote 메소드를 호출하고 테이블뷰를 ","type":"text"},{"code":"reload","type":"codeVoice"},{"type":"text","text":" 시켜주었다."}]}]}]},{"type":"thematicBreak"},{"name":"Note","style":"note","type":"aside","content":[{"text":"검색어가 없는 경우에는 메모의 전체를 다시 가져올 순 없을까…?","anchor":"%EA%B2%80%EC%83%89%EC%96%B4%EA%B0%80-%EC%97%86%EB%8A%94-%EA%B2%BD%EC%9A%B0%EC%97%90%EB%8A%94-%EB%A9%94%EB%AA%A8%EC%9D%98-%EC%A0%84%EC%B2%B4%EB%A5%BC-%EB%8B%A4%EC%8B%9C-%EA%B0%80%EC%A0%B8%EC%98%AC-%EC%88%9C-%EC%97%86%EC%9D%84%EA%B9%8C","type":"heading","level":3}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"검색어를 지워도 아까 검색했던 메모만 남아있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"즉, 다시 메모의 전체 목록을 보려면 앱을 껐다 켜야하는… 현상이 나타난 것이다.","type":"text"}]}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/UXYO5a0.gif"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"따라서 "},{"isActive":true,"type":"reference","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uisearchbardelegate"},{"type":"text","text":"를 활용해서 아래와 같이 구현해주었다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"code":["extension NotesViewController: UISearchBarDelegate {"," func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {"," guard searchBar.text == \"\" else { \/\/ 검색어가 비어있지 않다면 return"," return"," }"," PersistentManager.shared.setUpNotes() \/\/ 검색어가 비어있다면 다시 notes를 설정하는 메소드를 호출하고"," tableView.reloadData() \/\/ tableView를 reload한다."," }","}"],"syntax":"swift","type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"위 메소드는 텍스트가 변경될 때마다 호출되는 메소드이다."}],"type":"paragraph"}]}]},{"type":"aside","style":"note","content":[{"inlineContent":[{"type":"text","text":"검색어가 존재하지 않다면 전체목록을 보여주는 모습"}],"type":"paragraph"}],"name":"Note"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/ieDQeKV.gif"}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/ios-development.tistory.com\/94"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/zeddios.tistory.com\/1199","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/codershigh.dscloud.biz\/techblogs\/tb_009_UISearchController\/tb009_script.html"}]}]}],"type":"unorderedList"}]}]}],"kind":"content"}],"kind":"article","metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"title":"220222 UISearchController","roleHeading":"Article"},"sections":[],"references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://developer.apple.com/documentation/uikit/uisearchbardelegate":{"type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uisearchbardelegate","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uisearchbardelegate","title":"UISearchBarDelegate","titleInlineContent":[{"text":"UISearchBarDelegate","type":"text"}]},"https://developer.apple.com/documentation/uikit/uisearchresultsupdating/1618658-updatesearchresults":{"type":"link","identifier":"https:\/\/developer.apple.com\/documentation\/uikit\/uisearchresultsupdating\/1618658-updatesearchresults","url":"https:\/\/developer.apple.com\/documentation\/uikit\/uisearchresultsupdating\/1618658-updatesearchresults","title":"updateSearchResults(for:)","titleInlineContent":[{"text":"updateSearchResults(for:)","type":"text"}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/qCHMrvk.gif":{"type":"image","identifier":"https:\/\/i.imgur.com\/qCHMrvk.gif","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/qCHMrvk.gif","traits":["1x","light"]}]},"https://i.imgur.com/UXYO5a0.gif":{"type":"image","identifier":"https:\/\/i.imgur.com\/UXYO5a0.gif","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/UXYO5a0.gif","traits":["1x","light"]}]},"https://i.imgur.com/ieDQeKV.gif":{"type":"image","identifier":"https:\/\/i.imgur.com\/ieDQeKV.gif","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/ieDQeKV.gif","traits":["1x","light"]}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220224_swiftui,_property_wrapper,_divider,_list,_geometryreader.json b/docs/data/documentation/arinote/220224_swiftui,_property_wrapper,_divider,_list,_geometryreader.json new file mode 100644 index 0000000..4f32f42 --- /dev/null +++ b/docs/data/documentation/arinote/220224_swiftui,_property_wrapper,_divider,_list,_geometryreader.json @@ -0,0 +1 @@ +{"metadata":{"modules":[{"name":"Ari's Notes"}],"title":"220224 SwiftUI, Property Wrapper, Divider, List, GeometryReader","roleHeading":"Article","role":"article"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220224_swiftui,_property_wrapper,_divider,_list,_geometryreader"]}],"schemaVersion":{"patch":0,"major":0,"minor":3},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220224_SwiftUI,_Property_Wrapper,_Divider,_List,_GeometryReader","interfaceLanguage":"swift"},"sections":[],"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/SwiftUI"]]},"primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","level":1,"type":"heading","text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"type":"text","text":"2월 24일 (목)"}]},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용","level":2,"type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SwiftUI"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Divider","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"List"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Property Wrapper"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"GeometryReader"}]}]}],"type":"unorderedList"}]}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"type":"heading","level":2,"text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[SwiftUI]"}]}],"type":"paragraph"},{"content":[{"inlineContent":[{"code":"애플에서 소개하는 SwiftUI","type":"codeVoice"},{"type":"text","text":" "},{"type":"text","text":"SwiftUl은 모든 Apple의 사용자 인터페이스를 선언하는 현대적인 방법입니다."},{"text":" ","type":"text"},{"type":"text","text":"연단 아름답고 역동적인 앱을 그 어느 때보다도 빠르게 만들 수 있습니다."}],"type":"paragraph"}],"type":"aside","name":"Note","style":"note"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"‘UIKit - MVC’ 구조는 event driven 방식인데, ‘SwiftUI - MVVM’ 구조는 data driven 방식이다.","type":"text"}]}]}],"type":"unorderedList"},{"content":[{"inlineContent":[{"type":"text","text":"왜 구조체로 정의했을까? 얻을 수 있는 이점은 무엇이 있을까?"}],"type":"paragraph"}],"type":"aside","style":"note","name":"Note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"뷰 사이에 retain cycle이 없다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SwiftUI는 특정 값이 변할 때 뷰를 다시 갱신해준다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"즉, 뷰를 다시 그려준다는 것인데, 새로 그려주는 과정 속에서 구조체로 그려준다면 비용이 저렴하다는 장점이 있다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이 말은 Heap에 갔다올 필요가 없어진다!","type":"text"}]}]}],"type":"unorderedList"}]}]},{"type":"aside","name":"Note","style":"note","content":[{"inlineContent":[{"type":"text","text":"Data Drive을 도와주는 친구, Property Wrapper"}],"type":"paragraph"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"@State","type":"codeVoice"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"@ObservableObject"}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"@ObservedObject"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"@Publushed","type":"codeVoice"}]}]},{"content":[{"inlineContent":[{"code":"@EnvironmentObject","type":"codeVoice"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"@StateObject"}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[SwfitUI - Property Wrapper]"}]}]},{"level":3,"anchor":"ObservableObject","type":"heading","text":"`ObservableObject`"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"필수 구현을 필요로 하지 않는 프로토콜"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Combine"},{"type":"text","text":"에 속한 기능이고 "},{"type":"codeVoice","code":"클래스"},{"type":"text","text":"에서만 사용 가능하다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"이 프로토콜을 준수한 클래스는 ","type":"text"},{"type":"codeVoice","code":"objectWillChange"},{"text":"라는 프로퍼티를 사용할 수 있다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"objectWillChange.send()를 이용하기 위함이다","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이 send()는 변경된 사항이 있다고 알려주는 기능을 한다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"그러나 변수도 많고 수정되는 부분이 복잡하다면 하나하나 신경쓰기 힘들어지는데, 이를 대신해주는 기능이 ","type":"text"},{"code":"@Published","type":"codeVoice"},{"text":" 프로퍼티 래퍼가 있다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"heading","anchor":"Published","level":3,"text":"`@Published`"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 변수가 변경되면 자동으로 "},{"code":"objectWillChange.send()","type":"codeVoice"},{"type":"text","text":"를 호출해준다."}]}]}],"type":"unorderedList"},{"anchor":"State","level":3,"type":"heading","text":"`@State`"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIKit에선 Property Observer를 통해서 변화가 일어나면 뷰를 업데이트 시키는 방식을 사용했지만 SwiftUI는 "},{"code":"@State","type":"codeVoice"},{"type":"text","text":"라는 프로퍼티 래퍼를 통해서 같은일을 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"@State로 선언된 변수의 값이 변할 때 View를 다시 계산해서 그려준다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"단 View의 body에서만 @State 변수에 접근해야한다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉 private 선언이 따라오는 것을 권장한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"외부에서 이 변수에 접근하면 안된다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"경고문, 텍스트필드, 편집모드 같이 현재 화면의 상태를 잠깐 나타내거나 간단한 View의 상태를 나타낼 변수를 선언하는데 적합하다."}]}]}],"type":"unorderedList"},{"content":[{"inlineContent":[{"text":"View는 구조체 타입이다. 그리고 화면에 보여지는 실질적인 뷰인 body 변수가 get-only라는 점 때문에 View의 내용을 수정할 수 없다. 그럼 View의 상태를 어떻게 변경해서 보여줄까?","type":"text"},{"type":"text","text":" "},{"type":"text","text":"구조체 타입이라서 참조를 가지고 있지 않기 때문에 변경사항을 적용해서 그려줄 때 원래 View에 추가하는 방식이 아니라 View를 새로 그려주는 방식을 취한다. 새로 그려주는데 복사를 해서 수정하려해도 변경을 해야하는데… body는 읽기전용이다. 이때 @State를 사용하면 된다."}],"type":"paragraph"}],"type":"aside","style":"note","name":"Note"},{"type":"aside","style":"note","name":"Note","content":[{"inlineContent":[{"text":"@State 변수는 Heap에 할당된다. View에는 포인터만 있고 새로운 View가 만들어지면 포인터를 새로운 View에 옮겨서 힙의 같은 메모리를 가르킨다면 데이터도 같을 것이다. 이런식으로 View의 상태를 저장하고 변경한다.","type":"text"}],"type":"paragraph"}]},{"anchor":"Binding","type":"heading","text":"`@Binding`","level":3},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"@State의 변수에 "},{"type":"codeVoice","code":"$"},{"text":" 달러표시를 붙여서 사용할 수 있는데, 프로퍼티 래퍼에 ","type":"text"},{"code":"$","type":"codeVoice"},{"text":"를 붙이면 ","type":"text"},{"code":"projectValue","type":"codeVoice"},{"text":"라고도 한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"code":"$","type":"codeVoice"},{"type":"text","text":" 달러표시를 붙이면 "},{"code":"Binding","type":"codeVoice"},{"text":" 타입의 변수가 나타나는 것을 볼 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"부모 View의 @State와 같은 값을 "},{"code":"양방향","type":"codeVoice"},{"text":"으로 연결되도록 해준다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["struct TestView: View {"," @State private var name: String = \"parent\""," var body: some View {"," ChildView(childName: $name) Text(name)"," }","}","","struct ChildView: View {"," @Binding var childName: String"," var body: some View {"," Button(\"Change Name\") {"," childName = \"child\""," }"," }","}"]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"위 코드에서 TestView가 ChildView를 포함하고 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"여기서 현재 TestView의 name은 parent이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ChildView의 childName도 같다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이때 ChildeView의 Button을 누르게 된다면?","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"TestView, ChildView의 name 모두 child로 변경된다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이렇듯 State의 "},{"code":"$","type":"codeVoice"},{"type":"text","text":" 달러표시는 다른 변수에 "},{"type":"codeVoice","code":"연결"},{"text":"을 해주는 역할을 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"즉, Binding은 다른 어딘가에 연결되어있는 값이고, 해당 값이 변경되면 연결된 모든 값들이 변경된다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"데이터에 대한 단일 소스를 갖는 형태이다. 같은 데이터를 가지고 있다면 한쪽에서 수정되면 당연히 같이 수정이 일어나야한다.","type":"text"}],"type":"paragraph"}]}]},{"text":"`@ObservedObject`","level":3,"anchor":"ObservedObject","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"대부분 ","type":"text"},{"type":"codeVoice","code":"ViewModel"},{"text":"을 선언할 때 사용하는 프로퍼티 래퍼이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"ObservableObject","type":"codeVoice"},{"text":" 프로토콜을 준수하는 타입에 사용할 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ViewModel에서 변경사항이 있다면 뷰를 다시 그릴 수 있도록 해주는 역할을 한다."}]}]}]},{"text":"`@StateObject`","type":"heading","level":3,"anchor":"StateObject"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"ObservedObject와 같은 역할을 한다. 단점을 보완해서 iOS14에서 추가된 기능이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"State + ObservedObject 느낌이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"차이점으로는 ObservedObject는 View가 새로 그려질 때 새로 생성될 수 있다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"View Life Cycle에 의존한다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그러나 StateObject는 View가 새로 그려질 때 State처럼 새로 그려지지 않고 참조를 가지고 있어서 새로 생성되지 않는다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"code":"View Life Cycle에 의존하지 않는다.","type":"codeVoice"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"즉 새로 생성된다 라는 차이가 있다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"데이터가 유실될 수 있는 문제점"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"code":"ViewModel이 생성될 때 작업이 많다면 비효율적인 성능","type":"codeVoice"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"위 두가지를 개선한 것이 StateObject라고 보면 된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"약간.. 모델이 계속 살아있을 수 있도록 하는… 싱글턴 패턴의 느낌인 것 같다.","type":"text"}]}]}]},{"level":3,"type":"heading","text":"`@EnvironmentObject`","anchor":"EnvironmentObject"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"보통 앱 전반에 걸쳐 공유되는 데이터에 사용된다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":".environmentObject(_:)"},{"text":"를 통해 값을 전달할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"전달하는 object는 ObservableObject 프로토콜을 준수해야 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"모든 View가 읽을 수 있는 shared data라고 보면 될 것 같다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[SwiftUI - Divider]","type":"text"}]}]},{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/swiftui\/divider","type":"text"}]},{"type":"paragraph","inlineContent":[{"text":"Divider는 하나의 콘텐츠와 다른 콘텐츠를 구분하는 뷰이다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"구분선 같은 느낌…?"},{"type":"text","text":" "},{"text":"간단하게 선언만 해서 사용해주면 된다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/iDWkwfm.png","type":"image"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/LvPUdIZ.png"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"위 사진을 보면 제목과 내용에 따라 구분선이 쭈욱.. 그어져있는 모습을 확인할 수 있다."}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[SwiftUI - List]","type":"text"}]}]},{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/swiftui\/list","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"이름 그대로 목록 인터페이스를 구현하기 위한 타입이다."},{"type":"text","text":" "},{"type":"codeVoice","code":"UITableView"},{"text":"와 하는 일이 상당히 비슷하다. 하지만 훨씬 간편하다.","type":"text"}]},{"code":["List { \/\/ 리스트 목록을 동적으로 만들기"," Toggle(isOn: $showFavoritesOnly) {"," Text(\"Favorites only\")"," }"," ForEach(fillteredLandmarks) { landmark in"," NavigationLink {"," LandmarkDetail(landmark: landmark)"," } label: {"," LandmarkRow(landmark: landmark)"," }"," }"," }"],"type":"codeListing","syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"위 코드에서는 List 내부에 ","type":"text"},{"code":"ForEach","type":"codeVoice"},{"type":"text","text":"를 활용하여 동적으로 리스트를 구성하고 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"landmark","type":"codeVoice"},{"text":"라는 타입을 이니셜라이저로 할당해주고, 터치시 ","type":"text"},{"type":"codeVoice","code":"Detail"},{"type":"text","text":"로 넘어가고, 셀의 View는"},{"type":"codeVoice","code":" LandmarkRow"},{"text":"라는 타입으로 설정해주고 있다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"Toggle"},{"text":"의 경우 ","type":"text"},{"code":"@State private var showFavoritesOnly = false","type":"codeVoice"},{"type":"text","text":" 프로퍼티를 설정해주고 있다."}],"type":"paragraph"}]}]}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/UBza4Sw.png"}]},{"syntax":"swift","code":["struct LandmarkRow: View {"," var landmark: Landmark"," "," var body: some View {"," HStack {"," landmark.image"," .resizable() \/\/이미지 사이즈를 다시 설정"," .frame(width: 50, height: 50) \/\/ 높이 50 너비 50"," Text(landmark.name)"," Spacer() \/\/ 맨 뒤에 스페이서 추가하여 행을 완성"," if landmark.isFavorite { \/\/ 즐겨찾기 기능"," Image(systemName: \"star.fill\")"," .foregroundColor(.yellow)"," }"," }"," }","}"],"type":"codeListing"},{"items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"LandmarkRow"},{"type":"text","text":"의 경우 "},{"type":"codeVoice","code":"HStack"},{"text":" 안에 ","type":"text"},{"code":"Image","type":"codeVoice"},{"text":"와 ","type":"text"},{"type":"codeVoice","code":"Text"},{"type":"text","text":"를 활용하여 셀 구성을 하고 있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[GeometryReader란 무엇인가?]","type":"text"}],"type":"strong"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/tutorials\/swiftui\/drawing-paths-and-shapes"}]},{"content":[{"inlineContent":[{"type":"text","text":"SwiftUI의 튜토리얼을 따라하다가 발견한 키워드. 이게 뭐하는 놈인지 도대체가 모르겠어서 한번 찾아보았다."}],"type":"paragraph"}],"style":"note","name":"Note","type":"aside"},{"type":"paragraph","inlineContent":[{"text":"SwiftUI에선 Uikit으로 레이아웃을 작성할 때와 달리 뷰 객체에 직접 접근할 수 없다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":"뷰 객체에 직접 접근해 뷰 정보(size, position 등)에 알 수 있었던 것과 달리 SwiftUI에서 뷰 객체는 일시적인(transient) 객체로 프레임워크가 뷰를 그리고 난 후 객체는 사라지기 때문에 UiKit과 같은 방식으론 뷰에 관한 정보를 알 수 없다."}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"그렇다고 모든 뷰에 "},{"type":"codeVoice","code":".frame(width:. height:)"},{"type":"text","text":" 변경자를 사용해 직접 고정값을 넣어 하드코딩할 수도 없는 일이다. 이를 위해 등장한 개념이 GeometryReader로 상위뷰의 Geometry 정보를 하위뷰에 제공하는 역할을 한다."}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Size와 Position을 사용하여 Child View의 레이아웃을 설정할 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Content를 자신의 size 및 coordinate 공간의 함수로 정의하는 container view (이게 뭔말이여…)"}],"type":"paragraph"}]}],"type":"unorderedList"},{"style":"note","content":[{"type":"paragraph","inlineContent":[{"text":"앱의 다른 위치 또는 다른 크기의 디스플레이에서 view를 재사용할 때 올바르지 않을 수 있는 하드 코딩 번호 대신 GeometryReader를 사용하여 view를 동적으로 그리거나 position를 지정하고 size를 조정할 수 있다.","type":"text"},{"type":"text","text":" "},{"text":"GeometryReader는 parent view와 장치에 대한 크기 및 위치 정보를 동적으로 reports하며 size가 변경될 때마다 업데이트 된다","type":"text"},{"text":" ","type":"text"},{"text":"(예: 사용자가 iPhone을 회전할 때).","type":"text"}]}],"name":"Note","type":"aside"},{"type":"thematicBreak"},{"level":2,"anchor":"%EB%8A%90%EB%82%80-%EC%A0%90","text":"느낀 점","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"SwiftUI는 다 좋은데, 공식문서에서 설명해주는 개념들이 미완성된 모호한 정의를 내리고 있는게 있어서.. 헷갈리다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"구글링도 몇개 안나오는 듯한 느낌이다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"코딩하다 문제가 생겼을 때 진짜 삽질해야될 것만 같은 느낌적인 느낌…"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Preview는 잡다한 버그가 많다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그외에는 정말 뷰그리는 건 신세계 👍🏻"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/nsios.tistory.com\/145?category=922970","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/stackoverflow.com\/questions\/62544115\/what-is-the-difference-between-observedobject-and-stateobject-in-swiftui","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/jaesung0o0.medium.com\/swiftui-data-flow-stateobject-vs-observedobject-e32a37d80dd2"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/swiftui\/managing-model-data-in-your-app"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/www.hackingwithswift.com\/quick-start\/swiftui\/whats-the-difference-between-observedobject-state-and-environmentobject"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/www.hohyeonmoon.com\/blog\/swiftui-data-flow\/"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/www.hohyeonmoon.com\/blog\/swiftui-data-flow\/"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@sjoonb\/SwiftUI-GeometryReader"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/protocorn93.github.io\/2020\/07\/26\/GeometryReader-in-SwiftUI\/"}],"type":"paragraph"}]}]}]}]}]}],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/iDWkwfm.png":{"type":"image","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/iDWkwfm.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/iDWkwfm.png"},"https://i.imgur.com/UBza4Sw.png":{"alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/UBza4Sw.png"}],"identifier":"https:\/\/i.imgur.com\/UBza4Sw.png","type":"image"},"doc://AriNote/documentation/AriNote/SwiftUI":{"role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/SwiftUI","url":"\/documentation\/arinote\/swiftui","title":"SwiftUI","kind":"article","abstract":[{"type":"text","text":"언능 UIKit만큼 SwiftUI를 더 잘 알고 싶다!"}],"type":"topic"},"https://i.imgur.com/LvPUdIZ.png":{"alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/LvPUdIZ.png"}],"identifier":"https:\/\/i.imgur.com\/LvPUdIZ.png","type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220225_greedy,_wholenumbervalue,_enumerated,_reduce.json b/docs/data/documentation/arinote/220225_greedy,_wholenumbervalue,_enumerated,_reduce.json new file mode 100644 index 0000000..06a5432 --- /dev/null +++ b/docs/data/documentation/arinote/220225_greedy,_wholenumbervalue,_enumerated,_reduce.json @@ -0,0 +1 @@ +{"sections":[],"schemaVersion":{"major":0,"minor":3,"patch":0},"primaryContentSections":[{"kind":"content","content":[{"level":1,"type":"heading","text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"type":"text","text":"2월 25일 (금)"}]},{"level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading","text":"학습 내용"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Greedy Algorithm"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"코딩테스트 풀이 중 변환 꿀팁~"}]}]}],"type":"unorderedList"},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","type":"heading","level":2},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Greedy Algorithm]"}]}],"type":"paragraph"},{"style":"note","name":"Note","type":"aside","content":[{"inlineContent":[{"type":"text","text":"이번주차 알고리즘 문제가 그리디인데… 공부가 필요할 것 같아서…."}],"type":"paragraph"}]},{"text":"탐욕 알고리즘(Greedy Algorithm)","type":"heading","level":3,"anchor":"%ED%83%90%EC%9A%95-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98Greedy-Algorithm"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Greedy는 ‘탐욕스러운, 욕심많은’이란 뜻이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"말 그대로 선택의 순간마다 당장 눈앞에 보이는 최적의 상황만을 쫒아 최종적인 해답에 도달하는 방법이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"최적해를 구하는데에 사용되는 근사적인 방법","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"탐욕 알고리즘은 여러 경우 중 하나를 결정해야 할 때마다 그 순간에 최적이라고 생각되는 것을 선택해 나가는 방식으로 진행하여 최종적인 해답에 도달한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"순간마다 하는 선택은 그 순간에 대해 지역적으로는 최적이지만, 그 선택들을 계속 수집하여 최종적인 해답을 만들었다고 해서, 그것이 최적이라는 보장이 없다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"하지만 탐욕 알고리즘을 적용할 수 있는 문제들은 지역적으로 최적이면서 전역적으로 최적인 문제들이다.","type":"text"}],"type":"paragraph"}]}]},{"type":"heading","level":3,"anchor":"%ED%83%90%EC%9A%95-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EB%AC%B8%EC%A0%9C%EB%A5%BC-%ED%95%B4%EA%B2%B0%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95","text":"탐욕 알고리즘 문제를 해결하는 방법"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"선택 절차(Selection Procedure)"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"현재 상태에서의 최적의 해답을 선택"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"적절성 검사(Feasibility Check)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"선택된 해가 문제의 조건을 만족하는지 검사","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"해답 검사(Solution Check)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"원래 문제가 해결되었는지 검사하고, 해결되지 않았다면 선택절차로 돌아가 위의 과정을 반복","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"level":3,"text":"탐욕 알고리즘을 적용하려면 필요한 조건","type":"heading","anchor":"%ED%83%90%EC%9A%95-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%98%EB%A0%A4%EB%A9%B4-%ED%95%84%EC%9A%94%ED%95%9C-%EC%A1%B0%EA%B1%B4"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"탐욕 알고리즘이 잘 작동하는 문제는 대부분 탐욕스런 선택 조건(greedy choice property)과 최적 부분 구조 조건(Optimal Substructure) 이라는 두가지 조건이 만족된다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"탐욕스런 선택 조건은 앞의 선택이 이후의 선택에 영향을 주지 않는다는 것이며, 최적 부분 구조 조건은 문제에 대한 최적해가 부분 문제에 대해서도 역시 최적해라는 것이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"탐욕적 선택 속성(Greedy Choice Property)"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앞의 선택이 이후의 선택에 영향을 주지 않는다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"최적 부분 구조(Optimal Substructure)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"문제에 대한 최종 해결 방법은 부분 문제에 대한 최적 문제 해결 방법으로 구성된다."}]}]}]}]},{"content":[{"inlineContent":[{"text":"이러한 조건이 성립하지 않는 경우에는 탐욕 알고리즘은 최적해를 구하지 못한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"하지만 이러한 경우에도 탐욕 알고리즘은 근사 알고리즘으로 사용이 가능할 수 있으며, 대부분의 경우 계산 속도가 빠르기 때문에 실용적으로 사용할 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 경우 역시 어느 정도까지 최적해에 가까운 해를 구할 수 있는 지를 보장하려면 엄밀한 증명이 필요하다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"어떤 특별한 구조가 있는 문제에 대해서는 탐욕 알고리즘이 언제나 최적해를 찾아낼 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 구조를 "},{"type":"codeVoice","code":"매트로이드"},{"type":"text","text":"라 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"매트로이드는 모든 문제에서 나타나는 것은 아니나, 여러 곳에서 발견되기 때문에 탐욕 알고리즘의 활용도를 높여준다."}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"탐욕 알고리즘은 항상 최적의 결과를 도출하는 것은 아니지만, 어느정도 최적에 근사한 값을 빠르게 도출할 수 있는 장점이 있다."},{"type":"text","text":" "},{"text":"이 장점으로 인해 탐욕 알고리즘은 근사 알고리즘으로 사용할 수 있다.","type":"text"}],"type":"paragraph"}],"type":"aside","style":"note","name":"Note"},{"type":"aside","name":"Note","content":[{"inlineContent":[{"text":"탐욕 알고리즘을 적용해도 언제나 최적해를 구할 수 있는 문제(매트로이드)가 있고, 이러한 문제에 탐욕 알고리즘을 사용해서 빠른 계산 속도로 답을 구할 수 있다. 그래서 실용적으로 사용할 수 있다.","type":"text"}],"type":"paragraph"}],"style":"note"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[알고리즘 꿀팁]"}],"type":"strong"}]},{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/swift\/character\/3127025-wholenumbervalue"},{"text":" ","type":"text"},{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/swift\/array\/1687832-enumerated"}],"type":"paragraph"},{"code":["\/\/ String to [Int]","let arr: [Int] = \"12345\".compactMap { $0.wholeNumberValue } ","","\/\/ 배열 요소 인덱스 구하기 enumerated()","let withIndex = [\"일\",\"이\",\"삼\",\"사\",\"오\"].enumerated().map { print($0.offset, $0.element)}","\/*","0 일","1 이","2 삼","3 사","4 오","*\/","","\/\/ 이건 다시 복기용. reduce로 [Int] to String","let str: String = [1,2,3,4,5].reduce(\"\") { $0 + String($1) }"],"type":"codeListing","syntax":"swift"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/hanamon.kr\/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%83%90%EC%9A%95%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-greedy-algorithm\/","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","title":"220225 Greedy, wholeNumberValue, enumerated, reduce"},"kind":"article","identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220225_Greedy,_wholeNumberValue,_enumerated,_reduce"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220225_greedy,_wholenumbervalue,_enumerated,_reduce"]}],"references":{"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220228_usernotifications,_appdelegate.json b/docs/data/documentation/arinote/220228_usernotifications,_appdelegate.json new file mode 100644 index 0000000..ab71167 --- /dev/null +++ b/docs/data/documentation/arinote/220228_usernotifications,_appdelegate.json @@ -0,0 +1 @@ +{"schemaVersion":{"patch":0,"major":0,"minor":3},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220228_UserNotifications,_AppDelegate"},"metadata":{"title":"220228 UserNotifications, AppDelegate","role":"article","roleHeading":"Article","modules":[{"name":"Ari's Notes"}]},"sections":[],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220228_usernotifications,_appdelegate"]}],"primaryContentSections":[{"kind":"content","content":[{"level":1,"type":"heading","text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned"},{"inlineContent":[{"text":"2월 28일 (월)","type":"text"}],"type":"paragraph"},{"type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용"},{"items":[{"content":[{"inlineContent":[{"text":"User Notification","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"실습도 해보기~","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"type":"heading","text":"고민한 점 \/ 해결 방법","level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[User Notifications]"}]}]},{"type":"heading","text":"UserNotifications란?","level":3,"anchor":"UserNotifications%EB%9E%80"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"알림을 앱에서 띄우기 위한 프레임워크","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Notification은 서버에서 보낼 수도 있고, 앱 내에서도 보낼 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"앱이 구동중이든 아니든 중요한 정보를 사용자에게 전달할 때 활용할 수 있다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"예를 들어 스포츠 앱은 사용자에게 가장 좋아하는 팀의 점수를 푸시로 알려줄 수도 있다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Notification은 alert을 띄우고 소리를 낼 수도 있고, 앱 아이콘을 뱃지로 보여줄 수 있다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Hytaha6.png"}]},{"items":[{"content":[{"inlineContent":[{"text":"앱 내에서 서버에서 알림을 발생 시킬 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"앱 내부에서 알림을 띄운다면 앱은 Notification content를 생성하고 알림을 발생시킬 시간이나 지역과 같은 조건을 명시한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"앱 외부에서 보내는 Notification(push)은 알림을 보내기 위해 회사 서버를 사용하고, Apple Push Notification service(APNs)가 사용자 기기로의 알림 전달을 다룬다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":"이 프레임 워크는 아래의 것들을 할 때 사용한다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"앱이 지원하는 Notification의 타입을 정의한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Notification 타입과 관련있는 커스텀 action을 정의한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"앱 내부에서 알림을 띄우는 경우에 알림을 띄울 시간을 정의한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이미 전달된 알림을 처리한다."}]}]},{"content":[{"inlineContent":[{"text":"사용자가 선택하는 action에 반응한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"level":3,"anchor":"User-Notification%EC%9D%98-%EC%A2%85%EB%A5%98","type":"heading","text":"User Notification의 종류"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Local Notification"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"외부 서버를 통하지 않고 내부 앱에서 푸시 알림을 보내는 경우","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"미리알림, 알람앱의 푸시 알림 등…","type":"text"}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Remote Notification"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"외부 서버에서 APNs에 푸시 Notification을 보내주어 디바이스에 나타내는 경우(JSON 형태의 딕셔너리로 만들어 보내준다.)","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"로컬보다는 지연이 있고 손실될 우려가 있다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"게임에서의 이벤트 알림, 쿠폰 등등 여러 앱의 실사용에서 볼 수 있는 서버의 푸시 알림"}]}]}],"type":"unorderedList"}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[직접 사용해보기]","type":"text"}],"type":"strong"}]},{"text":"유저에게 알림 허락을 받아야한다. (권한을 얻어야한다.)","type":"heading","level":3,"anchor":"%EC%9C%A0%EC%A0%80%EC%97%90%EA%B2%8C-%EC%95%8C%EB%A6%BC-%ED%97%88%EB%9D%BD%EC%9D%84-%EB%B0%9B%EC%95%84%EC%95%BC%ED%95%9C%EB%8B%A4-%EA%B6%8C%ED%95%9C%EC%9D%84-%EC%96%BB%EC%96%B4%EC%95%BC%ED%95%9C%EB%8B%A4"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/vjttUhW.png","type":"image"}],"type":"paragraph"},{"code":["import UserNotifications","","let center = UNUserNotificationCenter.current()","center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in"," "," if let error = error {"," \/\/ 에러처리를 여기서..."," }",""," \/\/ 유저가 승인하면 여기서 어떠한 로직을 처리할 수가 있다. "," \/\/ 권한 부여에 따른 기능 활성화 등을 설정한다.","","}",""],"type":"codeListing","syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"알림을 보내겠다는 권한을 명시적으로 요청할 때 사용자는 앱이 보내는 알림을 보기도 전에 권한을 허용하거나 거절해야하는데, 권한을 요청하기 전에 context를 잘 설정했다고 해도 사용자는 이를 바로 결정하기 힘들 수도 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"options에 .provisional 이라는 옵션을 추가하면 임시 승인을 사용하여 알림을 보내겠다는 권한을 시도적으로 알림 권한을 요청할 때 활용할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"시스템은 이런 옵션의 알림을 조용히 보낸다. 소리나 배너를 띄우지 않고 잠금 화면에 띄우지 않는다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"대신 알림 센터 기록에 남겨둔다. 이런 알림은 사용자가 계속 알림을 받을건지 아니면 알림을 끌건지 선택하게 하는 버튼을 포함시킬 수 있다.","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/n1p0C7x.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Keep 버튼 터치시 시스템은 알림을 눈에 띄게 할지, 아니면 조용히 하게 할지 선택하게 한다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"만약 사용자가 눈에 띄게 하는 알림 설정을 하게된다면 앱은 provisional authorization에 포함된 모든 권한을 얻게된다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"사용자가 조용한 알림을 선택했다면 시스템은 앱이 알림을 보낼 수 있게 허용은 하지만 alert, sound, 앱아이콘 배지를 띄우지 않게 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이 경우 알림은 오직 알림 센터 기록에만 뜨게한다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":".provisional 이 옵션이 포함된다면 명시적으로 권한을 요청하는 것과는 다르게 사용자에게 알림을 받을 권한을 요청하지 않는다. 대신 이 메서드를 처음 호출할 때 자동으로 권한을 획득한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"하지만 사용자가 명시적으로 keep하거나 turn off 하기 전까지 권한 상태는 UNAuthorizationStatus.provisional이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"사용자가 권한 상태를 언제든지 바꿀 수 있기 때문에 local notification을 보내기 전에 상태를 계속 확인해야 한다.","type":"text"}]}]}]},{"text":"Push 알림 메세지를 설정해주자","anchor":"Push-%EC%95%8C%EB%A6%BC-%EB%A9%94%EC%84%B8%EC%A7%80%EB%A5%BC-%EC%84%A4%EC%A0%95%ED%95%B4%EC%A3%BC%EC%9E%90","level":3,"type":"heading"},{"syntax":"swift","code":["let content = UNMutableNotificationContent()","content.title = \"Weekly Staff Meeting\"","content.body = \"Every Tuesday at 2pm\""],"type":"codeListing"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"모든 push 알림에는 메세지가 존재한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"어떤 메세지를 가지고 유저에게 push 알림을 줄건지"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":"UNMutableNotificationContent를 사용하여 설정해줄 수 있다.","type":"text"}]},{"anchor":"%EC%95%8C%EB%A6%BC-%ED%8A%B8%EB%A6%AC%EA%B1%B0-%EC%84%A4%EC%A0%95","level":3,"type":"heading","text":"알림 트리거 설정"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Iah5Rit.png"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UserNotifications를 어떻게 작동할건지 트리거를 만들어야 한다."}]}]},{"content":[{"inlineContent":[{"text":"특정 시간, 시간 간격, 위치 변경을 기반으로 트리거를 설정할 수 있다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"syntax":"swift","code":["func scheduleNotification() {"," let center = UNUserNotificationCenter.current()",""," let content = UNMutableNotificationContent()"," content.title = \"Late wake up call\""," content.body = \"The early bird catches the worm, but the second mouse gets the cheese.\""," content.categoryIdentifier = \"alarm\""," content.userInfo = [\"customData\": \"fizzbuzz\"]"," content.sound = UNNotificationSound.default",""," var dateComponents = DateComponents()"," dateComponents.hour = 10"," dateComponents.minute = 30"," let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 5, repeats: false)",""," let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)"," center.add(request)","}"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"위 코드에서는 일정한 시간이 지나면 알림을 나타나게 설정되어있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"만약 특정 시간을 원한다면 아래와 같이 UNCalendarNotificationTrigger를 활용해볼 수도 있다."}]}]}]},{"code":["var dateComponents = DateComponents()","dateComponents.hour = 10","dateComponents.minute = 30","let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)"],"type":"codeListing","syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 설정은 repeats 설정이 true로 되어있기 때문에 매일 오전 10시 30분에 알림이 표시된다."}]}]}]},{"type":"heading","text":"알림 요청을 한다.","anchor":"%EC%95%8C%EB%A6%BC-%EC%9A%94%EC%B2%AD%EC%9D%84-%ED%95%9C%EB%8B%A4","level":3},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"위에서 만들어주었던 트리거를 가지고 알림을 요청(예약)해주는 작업을 해준다."}]}]}],"type":"unorderedList"},{"syntax":"swift","type":"codeListing","code":["let request = UNNotificationRequest(identifier: \"timerdone\", content: content, trigger: trigger)"]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"identifier는 알림 요청이 여러가지일 때 알림들을 구분할 수 있게 해주는 고유 식별자이다."}]}]}],"type":"unorderedList"},{"text":"요청했던 알림을 알림센터에 추가해준다.","level":3,"type":"heading","anchor":"%EC%9A%94%EC%B2%AD%ED%96%88%EB%8D%98-%EC%95%8C%EB%A6%BC%EC%9D%84-%EC%95%8C%EB%A6%BC%EC%84%BC%ED%84%B0%EC%97%90-%EC%B6%94%EA%B0%80%ED%95%B4%EC%A4%80%EB%8B%A4"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"UNUserNotificationCenter는 모든 알림 관련 동작을 관리하는 객체이다.","type":"text"}]}]},{"content":[]},{"content":[{"inlineContent":[{"type":"text","text":"유저가 알림을 받겠다고 허용한 경우 UNNotificationRequest는 UNNotification을 만드는데 사용되며 사용자에게 알려지게 된다."}],"type":"paragraph"}]}]},{"syntax":"swift","code":["UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"위와 같이 add라는 메소드를 사용해서 위에서 만들었던 request를 파라미터로 넣어주었다.","type":"text"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[UserNotifications 활동학습]","type":"text"}]}]},{"text":"application(_:didFinishLaunchingWithOptions:)","anchor":"applicationdidFinishLaunchingWithOptions","type":"heading","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"실행 프로세스가 거의 끝나고 앱이 실행될 준비가 거의 완료되었음을 알리는 메소드이다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"앱이 완전 종료된 상태에서 push를 클릭하여 앱을 실행한 경,우 원하는 페이지로 원활하게 이동시키려면 해당 메소드에서 알림 동의 여부를 사용자에게 요청하는 것이 적절하다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"aside","name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"먼저 AppDelegate.swift에 UserNotifications를 import 해준다."}]}],"style":"note"},{"code":["import UserNotifications"],"type":"codeListing","syntax":"swift"},{"name":"그리고 프로퍼티로 Center를 선언해주고, Application(_","style":"note","content":[{"inlineContent":[{"text":"didFinishLaunchingWithOptions:) 메소드에 아래와 같은 코드를 추가하여 앱을 처음 실행할 때 알림 환경을 설정하고 사용자 동의를 받도록 구현한다.","type":"text"}],"type":"paragraph"}],"type":"aside"},{"code":["class AppDelegate: UIResponder, UIApplicationDelegate {"," let center = UNUserNotificationCenter.current()","","\/\/ MARK: - UserNotification Authorization"," func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {"," center.delegate = self"," \/\/ 사용자에게 알림을 받겠냐는 승인 요청하기."," center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in"," "," if let error = error {"," \/\/ 에러처리를 여기서..."," print(error)"," }"," "," if granted {"," \/\/ 유저가 승인하면 여기서 어떠한 로직을 처리할 수가 있다."," \/\/ 권한 부여에 따른 기능 활성화 등을 설정한다."," DispatchQueue.main.async {"," application.registerForRemoteNotifications()"," }"," } else {"," \/\/ 유저가 승인을 거절했을 경우"," print(\"사용자가 push를 거절하였음.\")"," }"," }"," "," return true"," }","..."],"type":"codeListing","syntax":"swift"},{"type":"aside","name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ViewController로 돌아와서 알림 콘텐츠와 트리거를 생성해서 알람을 만들어준다"}]}],"style":"note"},{"syntax":"swift","type":"codeListing","code":["class ViewController: UIViewController {","\/\/ 콘텐츠 생성"," var yellowContent: UNMutableNotificationContent = {"," let content = UNMutableNotificationContent()"," content.title = \"로컬 알림 메세지\""," content.body = \"준비된 내용을 보려면 탭하세요...\""," content.userInfo = [\"target_view\" : \"yellow_view\"]"," return content"," }()","","\/\/ 트리거 생성"," let yellowTimeIntervalTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 2, repeats: false)","..."]},{"name":"Note","content":[{"inlineContent":[{"text":"그리고 위에서 만든 콘텐츠와 트리거로 UINotificationRequest를 선언하고, 알림센터에 등록해주는 작업까지 해준다.","type":"text"}],"type":"paragraph"}],"style":"note","type":"aside"},{"type":"codeListing","syntax":"swift","code":[" func setUpUserNotification() {"," let yellowRequest = UNNotificationRequest(identifier: \"yellowView\", content: yellowContent, trigger: yellowTimeIntervalTrigger)"," UNUserNotificationCenter.current().add(yellowRequest, withCompletionHandler: nil)"," }"]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 viewDidLoad 메소드에 위에서 만든 메소드를 호출해주면, 사용자가 알림을 허용하고나서 설정한대로 2초후 Push 알림이 뜨게 된다."}]}],"type":"aside","style":"note","name":"Note"},{"type":"codeListing","code":[" override func viewDidLoad() {"," super.viewDidLoad()"," setUpUserNotification()"," }"],"syntax":"swift"},{"type":"aside","content":[{"type":"paragraph","inlineContent":[{"code":"UNUserNotificationCenterDelegate","type":"codeVoice"},{"type":"text","text":"를 활용하여 "},{"type":"reference","isActive":true,"identifier":"https:\/\/developer.apple.com\/documentation\/usernotifications\/unusernotificationcenterdelegate\/1649501-usernotificationcenter"},{"text":" 메소드를 정의하여 push를 터치하였을 때 처리해줄 작업을 처리해줄 수 있다.","type":"text"}]}],"name":"Note","style":"note"},{"syntax":"swift","code":["\/\/ MARK: - UNUserNotificationCenterDelegate","extension AppDelegate: UNUserNotificationCenterDelegate {"," func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {"," \/\/ tabBarController 를 캐스팅하여 선언"," let tabBarController = UIApplication.shared.connectedScenes.first?.delegate"," .flatMap { $0 as? SceneDelegate }"," .flatMap { $0.window?.rootViewController }"," .flatMap { $0 as? UITabBarController }",""," \/\/ 여기도 마찬가지로 flatMap을 활용하여 바인딩하여 분기처리를 해주었다."," response.notification.request.content.userInfo[\"target_view\"]"," .flatMap { $0 as? String }"," .flatMap {"," switch $0 {"," case \"yellow_view\":"," \/\/ 딕셔너리 값이 yellow_view일 경우 탭의 1번째를 select하고, performSegue를 해주었다. 이 때 sender에 content의 타이틀과 바디를 넘겨주었다."," tabBarController?.selectedIndex = 1"," guard let navi = tabBarController?.selectedViewController as? UINavigationController else {"," return"," }"," let title = response.notification.request.content.title"," let body = response.notification.request.content.body"," navi.topViewController?.performSegue("," withIdentifier: $0,"," sender: (title, body)"," )"," case \"brown_view\":"," tabBarController?.selectedIndex = 2"," let title = response.notification.request.content.title"," let body = response.notification.request.content.body"," tabBarController?.selectedViewController?.performSegue("," withIdentifier: $0,"," sender: (title, body)"," )"," default:"," return"," }"," }"," "," "," \/\/ 앱이 메모리에 올라와있는지에 따라 아래처럼 분기처리를 해줄 수 있다."," \/*"," switch UIApplication.shared.applicationState {"," case .background:"," "," case .inactive:"," "," case .active:"," "," }"," *\/"," }"," "," \/\/앱이 foreground에 있을 때. 즉 앱안에 있어도 push알림을 받게 해주는 메소드."," func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {"," completionHandler([.alert, .sound, .badge])"," }"," ","}"],"type":"codeListing"},{"name":"Note","style":"note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 performSegue를 하기전에 "},{"type":"codeVoice","code":"prepare"},{"type":"text","text":"를 활용하여 전달받은 sender를 뷰컨으로 전달해주었다"}]}]},{"type":"codeListing","syntax":"swift","code":[" override func prepare(for segue: UIStoryboardSegue, sender: Any?) {"," guard let nextViewController = segue.destination as? YellowViewController,"," let sender = sender as? (String, String) else {"," return"," }"," nextViewController.setUp(data: sender)"," }"]},{"name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"YellowViewController의 코드는 아래와 같다."}]}],"style":"note","type":"aside"},{"code":["class YellowViewController: UIViewController {"," "," var userinfo: (String, String)?",""," @IBOutlet var titleLabel: UILabel!"," @IBOutlet var bodyLabel: UILabel!"," "," override func viewDidLoad() {"," super.viewDidLoad()"," titleLabel.text = userinfo?.0 \/\/ text 설정"," bodyLabel.text = userinfo?.1"," }",""," func setUp(data: (String, String)) {"," self.userinfo = data \/\/ 데이터를 할당하고"," }","}"],"syntax":"swift","type":"codeListing"},{"type":"aside","name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"요약 정리"}]}],"style":"note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"AppDelegate.swift에 알림을 받겠냐는 승인 요청을 구현한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"push","type":"codeVoice"},{"type":"text","text":"를 터치했을 때 어떤 처리를 해줄 건지 "},{"type":"codeVoice","code":"UNUserNotificationCenterDelegate"},{"text":"를 채택하여 메소드를 정의해준다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"알림 콘텐츠, 트리거를 선언하여 알림을 생성한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"위에서 생성한 UserNotification을 센터에 추가한다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"나의 경우 viewDidLoad에 했지만, 상황에 따라 앱이 백그라운드로 가는 시점 (applicationWillResignActive(_ application: UIApplication))에 등록해줄 수도 있고, 날짜나 시간에 따라 트리거를 설정하여 시점을 변경해줄 수 있다."}]}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/yoojin99.github.io\/app\/User-Notifications\/","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/zeddios.tistory.com\/157"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/www.hackingwithswift.com\/example-code\/system\/how-to-set-local-alerts-using-unnotificationcenter","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/green1229.tistory.com\/69"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/github.com\/outofcode-example\/iOS-PushSetting\/blob\/master\/AppDelegate.swift"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developerbee.tistory.com\/202"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/g-y-e-o-m.tistory.com\/74"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/github.com\/outofcode-example\/iOS-PushSetting\/blob\/master\/AppDelegate.swift","type":"text"}]}]}]}]}]}]}],"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"references":{"https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate/1649501-usernotificationcenter":{"identifier":"https:\/\/developer.apple.com\/documentation\/usernotifications\/unusernotificationcenterdelegate\/1649501-usernotificationcenter","titleInlineContent":[{"type":"text","text":"userNotificationCenter(_: didReceive: withCompletionHandler:)"}],"url":"https:\/\/developer.apple.com\/documentation\/usernotifications\/unusernotificationcenterdelegate\/1649501-usernotificationcenter","type":"link","title":"userNotificationCenter(_: didReceive: withCompletionHandler:)"},"https://i.imgur.com/vjttUhW.png":{"identifier":"https:\/\/i.imgur.com\/vjttUhW.png","type":"image","variants":[{"url":"https:\/\/i.imgur.com\/vjttUhW.png","traits":["1x","light"]}],"alt":null},"https://i.imgur.com/n1p0C7x.png":{"alt":null,"identifier":"https:\/\/i.imgur.com\/n1p0C7x.png","type":"image","variants":[{"url":"https:\/\/i.imgur.com\/n1p0C7x.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://i.imgur.com/Hytaha6.png":{"identifier":"https:\/\/i.imgur.com\/Hytaha6.png","type":"image","variants":[{"url":"https:\/\/i.imgur.com\/Hytaha6.png","traits":["1x","light"]}],"alt":null},"https://i.imgur.com/Iah5Rit.png":{"alt":null,"identifier":"https:\/\/i.imgur.com\/Iah5Rit.png","type":"image","variants":[{"url":"https:\/\/i.imgur.com\/Iah5Rit.png","traits":["1x","light"]}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220301_mvvm,_----_--.json b/docs/data/documentation/arinote/220301_mvvm,_----_--.json new file mode 100644 index 0000000..c56aa11 --- /dev/null +++ b/docs/data/documentation/arinote/220301_mvvm,_----_--.json @@ -0,0 +1 @@ +{"kind":"article","identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220301_MVVM,_----_--"},"primaryContentSections":[{"kind":"content","content":[{"level":1,"text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"3월 1일 (화)"}]},{"type":"heading","text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"MVVM에 대해서 알아보기"}]}]},{"content":[{"inlineContent":[{"text":"프로젝트 매니저 STEP2를 위한 설계 고민해보기","type":"text"}],"type":"paragraph"}]}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","type":"heading","level":2},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[MVVM 간단 요약 정리]"}]}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"View"},{"type":"text","text":"는 화면"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Model"},{"text":"은 화면에 그려지기 위한 데이터","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"ViewModel","type":"codeVoice"},{"type":"text","text":"은 화면에 보여질 데이터를 가공하는 역할을 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"View","type":"codeVoice"},{"type":"text","text":" -> "},{"type":"codeVoice","code":"ViewModel"},{"text":" -> ","type":"text"},{"code":"Model","type":"codeVoice"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Model은 3가지 형태로 나뉜다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"원본"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"원본에서 필요한 것만 필터링한 데이터","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ViewModel에서 가지고 있는 데이터"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ViewModel의 특징"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"화면에 그려야할 데이터를 가지고 있음","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"비즈니스 로직도 가지고 있음"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 View는 내가 갖고 있는 데이터를 가져다가 그리기만 해라~"}],"type":"paragraph"}]}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[리뷰어 찰리 리뷰 엿듣기]"}],"type":"strong"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"ViewModel에는 UiKit import 하지말기","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"ViewController에는 비즈니스 로직 넣지 말기"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ViewModel에게 이벤트만 전달하자"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"예를 들어 길게 터치했을 때… add버튼을 터치했을 때…등"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"어떤 라이브러리를 사용하든 ","type":"text"},{"code":"의존성","type":"codeVoice"},{"type":"text","text":"이 없어야 한다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Storage라는 것을 추상화해서 프로토콜로 구현","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이후 Firebase, CoreData 등으로 쉽게 갈아끼울 수 있도록 설계해보자"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Rx의 경우 오퍼레이터에 빠져서 집중할 필요 없다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"결국 Relay, Subject, Observable… 등 기본적인 것만 해도 충분히 쓸 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"type":"text","text":"STEP2를 위해 Model, ViewModel, View를 구성하기 위한 TodoList를 만들어 두었다."},{"text":" ","type":"text"},{"text":"일단 만들어보기는 했으나, 직접 코드를 적어가면서 좀더 수정이 필요할 것 같다.","type":"text"},{"text":" ","type":"text"},{"text":"설계하면서 고민했던 점은,","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"추상화가 가능한 부분인지?"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"View의 경우 중복되는 일부분을 CustomView로 만들어서 재사용을 할지, Controller 자체를 재사용할지…? (Add, Edit)"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"View는 그리기만 하고 있는지, 비즈니스 로직이 들어가지 않았는지 검수"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ViewModel의 분리"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":"곰튀김이 MVVM 전략에 대해 조언주셨던 부분도 복기해보았다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"설계 순서","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"모델"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"비즈니스 로직 (프로토콜로 추상화)"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"동작을 검증 (테스트)"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"UI를 붙인다. (뷰는 껍데기일 뿐)"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"구현~"}]}]}]}]}]},{"inlineContent":[{"type":"text","text":"이후에는 남은 코테문제들을 풀이했다."}],"type":"paragraph"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"곰튀김님 강의 복습"}]}]}]}]}],"type":"unorderedList"}]}],"metadata":{"role":"article","roleHeading":"Article","title":"220301 MVVM, 프로젝트 설계","modules":[{"name":"Ari's Notes"}]},"schemaVersion":{"patch":0,"minor":3,"major":0},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Architecture"]]},"sections":[],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220301_mvvm,_----_--"]}],"references":{"doc://AriNote/documentation/AriNote/Architecture":{"role":"collectionGroup","kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Architecture","abstract":[{"text":"공부 많이해서 상황에 따라 적절한 아키텍처를 선택해보자.","type":"text"}],"title":"Architecture","type":"topic","url":"\/documentation\/arinote\/architecture"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220303_clean_architecture_mvvm,_async,_await.json b/docs/data/documentation/arinote/220303_clean_architecture_mvvm,_async,_await.json new file mode 100644 index 0000000..bd1694f --- /dev/null +++ b/docs/data/documentation/arinote/220303_clean_architecture_mvvm,_async,_await.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Architecture"]]},"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220303_Clean_Architecture_MVVM,_async,_await","interfaceLanguage":"swift"},"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"220303 Clean Architecture MVVM, async, await"},"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)p","type":"heading","anchor":"TIL-Today-I-Learnedp","level":1},{"inlineContent":[{"type":"text","text":"3월 3일 (목)"}],"type":"paragraph"},{"text":"학습 내용","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"오늘은 하루종일 ViewModel 설계 삽질했다ㅎㅎ"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"비비 특강"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"Concurrency await async actor","type":"codeVoice"}]}]}],"type":"unorderedList"}]}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2,"type":"heading","text":"고민한 점 \/ 해결 방법"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[Clean architecture MVVM]","type":"text"}]}]},{"type":"paragraph","inlineContent":[{"text":"Rx로 ViewModel 구현해보겠다고 설치다가 결국엔 다 지웠다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"일단 Rx없이 ViewModel을 구현하고 이후 리팩토링을 해야겠다…"},{"type":"text","text":" "},{"text":"다 지우고 클린 아키텍처라도 적용해보자고 맘먹고 ViewModel을 짜보았다.","type":"text"},{"type":"text","text":" "},{"text":"(근데 시간이 될까..?)","type":"text"}]},{"anchor":"MVVM%EC%9D%84-%EC%99%9C-%EC%93%B8%EA%B9%8C","level":3,"type":"heading","text":"MVVM을 왜 쓸까?"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"구체적인 View를 알지 못하더라도 요구사항에 따라 ViewModel을 설계할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"ViewModel을 재사용 단위로 설계할 수도 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"Testable하다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"MVVM을 쓰는 가장 큰 이유는 Command 패턴과 Data Binding이 핵심이기 때문","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"View에서 명령을 주면 ViewModel에서 처리하고 데이터를 View에 알아서 바인딩 해주는 시스템"}],"type":"paragraph"}]}]},{"anchor":"Presentation","text":"Presentation","type":"heading","level":3},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"View","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"ViewModel: UI 이벤트가 발생하면 어떤 이벤트인지 판단 후 UseCase를 요청한 후 View에 업데이트를 하라고 알려줌"}],"type":"paragraph"}]}],"type":"unorderedList"},{"level":3,"text":"Domain","type":"heading","anchor":"Domain"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"UseCase: 비즈니스 로직","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Repository : 도메인과 데이터의 인터페이스 역할"}],"type":"paragraph"}]}]}]}]},{"anchor":"Data","level":3,"text":"Data","type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Storage : 데이터 입출력이 실행되는 곳"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"영구 저장소, 네트워크 등…","type":"text"}],"type":"paragraph"}]}]}]}]},{"anchor":"%EB%8D%B0%EC%9D%B4%ED%84%B0-%ED%9D%90%EB%A6%84","level":3,"type":"heading","text":"데이터 흐름"},{"items":[{"content":[{"inlineContent":[{"text":"View는 ViewModel의 메소드를 호출","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"ViewModel에서 어떤 이벤트인지 판단한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이벤트에 따라 UseCase를 실행"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UseCase는 User와 Repository를 결합"}]}]},{"content":[{"inlineContent":[{"text":"각 Repository는 네트워크, DB관련 스토리지, 메모리 관련 Storage에서 데이터 반환","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"다시 UI에 업데이트"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Storage -> Repository -> UseCase -> ViewModel -> View","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/aDHA7m0.png"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/9VG3JyK.png","type":"image"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"나도 한번 따라 만들어봤는데, 뭔가 기능이 중첩되는 느낌…. 아직 메모리 스토리지라 그런건가…. 파이어베이스나 코어데이터 생기면 그럴싸해질까…?","type":"text"}]}],"name":"Note","type":"aside","style":"note"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/5S1tmWk.png"}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[Concurrency await async actor]"}],"type":"strong"}]},{"level":3,"text":"동기 프로그래밍이란?","type":"heading","anchor":"%EB%8F%99%EA%B8%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%B4%EB%9E%80"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"프로그램의 흐름과 이벤트의 발생 및 처리를 종속적으로 수행하는 방법","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"sync programming","type":"text"},{"text":" ","type":"text"},{"identifier":"https:\/\/i.imgur.com\/48YkDHs.png","type":"image"}]}]}]},{"type":"heading","text":"비동기 프로그래밍이란?","anchor":"%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%B4%EB%9E%80","level":3},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"프로그램의 흐름과 이벤트의 발생 및 처리를 독립적으로 수행하는 방법"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"async Programming"},{"type":"text","text":" "},{"type":"image","identifier":"https:\/\/i.imgur.com\/pIUIBPx.png"}]}]}]},{"type":"codeListing","syntax":null,"code":["* 비동기 프로그래밍"," * 프로그램의 흐름과 이벤트의 발생 및 처리를 독립적으로 수행하는 방법"," ","* 동시성 프로그래밍"," * 여러 작업이 논리적인 관점에서 동시에 수행되는 것"," 싱글 코어 또는 멀티 코어에서 멀티 스레딩을 하기 위해 적용"," ","* 병렬성 프로그래밍"," * 여러 작업이 물리적인 관점에서 동시에 수행되는 것"]},{"type":"thematicBreak"},{"anchor":"%EA%B8%B0%EC%A1%B4%EC%9D%98-%EB%8F%99%EC%8B%9C%EC%84%B1-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D%EC%9D%98-%EB%AC%B8%EC%A0%9C","level":3,"type":"heading","text":"기존의 동시성 프로그래밍의 문제"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"과도한 중첩 (a.k.a. 장풍 코드)"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"오류 처리의 어려움"}]}]}],"type":"unorderedList"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/plURgxQ.png","type":"image"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/eAvZzw0.png"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"조건부 실행의 어려움","type":"text"}]}]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/dk0bU16.png","type":"image"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"swizzle"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"런타임 시점에 조건에 따라서 다른 메소드로 바꿔서 실행한다."}]}]}]}]}]},{"type":"paragraph","inlineContent":[{"text":"함수의 실행 흐름이 자연스럽지 못하다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"실수하기 쉽다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/pWIZUTz.jpg","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"하지만 async await을 사용하면 동기코드처럼 작성할 수 있다."}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/wv4cHt2.png"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"간단히 살펴보는 문법"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/zTK5oQB.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"get만 가능!"}]}]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/PSYJDZ7.png","type":"image"}],"type":"paragraph"},{"type":"thematicBreak"},{"text":"동작 흐름","level":3,"type":"heading","anchor":"%EB%8F%99%EC%9E%91-%ED%9D%90%EB%A6%84"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"async는 함수를 suspend 시킬 수 있다."}]}]},{"content":[{"inlineContent":[{"text":"await 키워드를 만나면 스레드 block을 해제하고 suspend 된다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉 await 키워드가 suspend를 시키는 지점"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"suspend 된 함수가 있을 때, 다른 작업은 진행될 수 있다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉 비동기적으로 작업을 할 수 있다."}]}]}]}]}]},{"name":"Note","content":[{"inlineContent":[{"type":"text","text":"스레드 block을 해제하고 suspend 된다. -> 함수가 스레드에 대한 제어권을 포기한다."}],"type":"paragraph"}],"style":"note","type":"aside"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이 개념은 코루틴(Coroutine)에서 출발했는데…"},{"text":" ","type":"text"},{"text":"Swift의 async\/await은 코루틴 모델을 적용했다.","type":"text"}],"type":"paragraph"}]}]},{"text":"코루틴(Coroutine)이란?","level":3,"type":"heading","anchor":"%EC%BD%94%EB%A3%A8%ED%8B%B4Coroutine%EC%9D%B4%EB%9E%80"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"루틴(소프트웨어에서 특정 동작을 수행하는 일정 코드 부분)의 일종","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"두 개 이상의 루틴이 서로를 호출하는 관계","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"서브 루틴을 구분할 수 없다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"A를 프로그래밍할 때는 B를 A의 서브루틴으로 생각 (반대도 성립)","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"실행되는 코루틴은 이전에 자신의 실행이 마지막으로 중단되었던 지점 다음의 장소에서 실행을 재개"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이게 가장 중요한 부분."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"중간에 suspend되었다가 다시 실행될 수 있다."}],"type":"paragraph"}]}]}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/K61HYy5.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"동기함수는 스레드의 제어권이 계속 남아있다.","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/QpHUXCp.png","type":"image"}]},{"items":[{"content":[{"inlineContent":[{"text":"await키워드를 만나면 스레드에 대한 제어권을 포기한다고 했다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"즉 스레드에 대한 제어권을 System에게 맡긴다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"그리고 작업이 완료되었다면 resume을 통해서 다시 돌아와서, 처음 suspend된 시점부터 시작되어 돌아간다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"async 함수가 호출된 쓰레드가 메인이라면 await 이후에도 메인쓰레드에서 실행된다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"async 프로퍼티도 가능!"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"text":"[느낀 점]","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"비동기 코드가 동기 코드처럼 반환값을 가질 수 있게되었다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이전엔 DispatchQueue로 global 스레드에 작업을 보내서 처리해준 부분이 await 키워드만 있으면, await 시점부터는 system이 알아서 스레드 관리를 해주는 느낌이다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"하지만.. 나온지 얼마안되서 아직은 개인 프로젝트에서만 활용할 수 있을 것 같다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 wwdc 영상을 꼭 보아라…"}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/Lvu06kh.png"}],"type":"paragraph"},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/ios-development.tistory.com\/555?category=978961"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/tech.olx.com\/clean-architecture-and-mvvm-on-ios-c9d167d9f5b3"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/github.com\/iamchiwon\/whatisarchitecture"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/github.com\/kudoleh\/iOS-Clean-Architecture-MVVM"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"schemaVersion":{"patch":0,"major":0,"minor":3},"sections":[],"variants":[{"paths":["\/documentation\/arinote\/220303_clean_architecture_mvvm,_async,_await"],"traits":[{"interfaceLanguage":"swift"}]}],"references":{"https://i.imgur.com/Lvu06kh.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/Lvu06kh.png"}],"identifier":"https:\/\/i.imgur.com\/Lvu06kh.png","type":"image","alt":null},"https://i.imgur.com/pWIZUTz.jpg":{"variants":[{"url":"https:\/\/i.imgur.com\/pWIZUTz.jpg","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/pWIZUTz.jpg","type":"image","alt":null},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/pIUIBPx.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/pIUIBPx.png"}],"identifier":"https:\/\/i.imgur.com\/pIUIBPx.png","type":"image","alt":null},"https://i.imgur.com/48YkDHs.png":{"variants":[{"url":"https:\/\/i.imgur.com\/48YkDHs.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/48YkDHs.png","type":"image","alt":null},"https://i.imgur.com/K61HYy5.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/K61HYy5.png"}],"identifier":"https:\/\/i.imgur.com\/K61HYy5.png","type":"image","alt":null},"https://i.imgur.com/aDHA7m0.png":{"variants":[{"url":"https:\/\/i.imgur.com\/aDHA7m0.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/aDHA7m0.png","type":"image","alt":null},"https://i.imgur.com/9VG3JyK.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/9VG3JyK.png"}],"identifier":"https:\/\/i.imgur.com\/9VG3JyK.png","type":"image","alt":null},"https://i.imgur.com/zTK5oQB.png":{"variants":[{"url":"https:\/\/i.imgur.com\/zTK5oQB.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/zTK5oQB.png","type":"image","alt":null},"https://i.imgur.com/eAvZzw0.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/eAvZzw0.png"}],"identifier":"https:\/\/i.imgur.com\/eAvZzw0.png","type":"image","alt":null},"https://i.imgur.com/dk0bU16.png":{"variants":[{"url":"https:\/\/i.imgur.com\/dk0bU16.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/dk0bU16.png","type":"image","alt":null},"doc://AriNote/documentation/AriNote/Architecture":{"role":"collectionGroup","kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Architecture","abstract":[{"text":"공부 많이해서 상황에 따라 적절한 아키텍처를 선택해보자.","type":"text"}],"title":"Architecture","type":"topic","url":"\/documentation\/arinote\/architecture"},"https://i.imgur.com/plURgxQ.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/plURgxQ.png"}],"identifier":"https:\/\/i.imgur.com\/plURgxQ.png","type":"image","alt":null},"https://i.imgur.com/wv4cHt2.png":{"variants":[{"url":"https:\/\/i.imgur.com\/wv4cHt2.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/wv4cHt2.png","type":"image","alt":null},"https://i.imgur.com/PSYJDZ7.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/PSYJDZ7.png"}],"identifier":"https:\/\/i.imgur.com\/PSYJDZ7.png","type":"image","alt":null},"https://i.imgur.com/QpHUXCp.png":{"variants":[{"url":"https:\/\/i.imgur.com\/QpHUXCp.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/QpHUXCp.png","type":"image","alt":null},"https://i.imgur.com/5S1tmWk.png":{"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/5S1tmWk.png"}],"identifier":"https:\/\/i.imgur.com\/5S1tmWk.png","type":"image","alt":null}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220304_rxswift,_observable,_subject.json b/docs/data/documentation/arinote/220304_rxswift,_observable,_subject.json new file mode 100644 index 0000000..3e8df2f --- /dev/null +++ b/docs/data/documentation/arinote/220304_rxswift,_observable,_subject.json @@ -0,0 +1 @@ +{"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220304_RxSwift,_Observable,_Subject","interfaceLanguage":"swift"},"primaryContentSections":[{"content":[{"level":1,"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"3월 4일 (금)"}]},{"text":"학습 내용","type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"RxSwift를 활용하여 ViewModel 리팩토링 하기","type":"text"}],"type":"paragraph"}]}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","type":"heading","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"기존에는 Observable을 직접 구현하여 bind를 해주고 있었으나, Rx로도 할 수 있는 걸 왜.. 직접 구현하고 있지? 라는 의문이 들었다. 모른다고 회피하다가… 찝찝했는지 ViewModel을 꼭!!! Rx로 리팩토링 꼭!!! 해보고싶어서 삽질을 시작하게 되었다.","type":"text"}]}]}]},{"code":["final class ProjectListViewModel {","..."," var deleted: Observable","...","","class ViewController: UIViewController {"," "," func viewDidLoad() {"," viewModel.deleted.asObservable()"," .subscribe(onNext: { indexPath in"," self.tableView.deleteRows(at: [indexPath], with: .fade)"," }).disposed(by: bag)",""," }","..."],"syntax":"swift","type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"일단 ViewController에서는 위와 같은 방식으로 바인딩 해주어서 사용해주고 싶었다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"처음에는 직접 구현한 Observable이 아니라 Rx의 Observable을 활용해보려고 했었는데, 실패했다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"이유","type":"codeVoice"},{"type":"text","text":" 그 이유는 Observable의 경우 값을 넘겨주는 역할만 하지 값을 외부에서 받아들여서 넘겨주는 역할은 하지 않기 때문이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"해결"},{"text":" 따라서 값을 받아먹을 수 있으면서 이 값을 외부에서 컨트롤할 수 있는 것이 뭐가 있을까 찾아보다가 ","type":"text"},{"type":"codeVoice","code":"Subject"},{"text":"라는 오퍼레이터를 알게되었다.","type":"text"}]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[RxSwift로 데이터 바인딩 해보기]"}]}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터 바인딩을 통하여 테이블뷰의 delete 이벤트가 발생되면, 그에 따라 데이터도 제거해주고, 해당하는 셀이 알아서 제거될 수 있도록 해볼 것이다."}]},{"type":"paragraph","inlineContent":[{"text":"먼저 ViewModel에 셀을 제거하기 위해 필요한 IndexPath 데이터를 가지고 있는 ","type":"text"},{"code":"PublishSubject","type":"codeVoice"},{"type":"text","text":"를 생성한다."}]},{"type":"codeListing","code":["final class ProjectListViewModel {"," var deleted = PublishSubject()","\/\/ ..."],"syntax":"swift"},{"type":"aside","style":"note","name":"Note","content":[{"inlineContent":[{"code":"Subject","type":"codeVoice"},{"text":"란?","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"Observable은 값을 넘겨주는 역할을 하지, 값을 외부에서 받아들여서 넘겨주는 역할은 하지않는다. 그래서 Observable처럼 값을 받아먹을 수는 있는 애인데 외부에서 이 값을 컨트롤할 순 없을까? 하고 나온 것이 Subject이다. Observable과 Observer역할을 동시에 수행한다."}],"type":"paragraph"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"총 4가지의 종류가 있다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"AsyncSubject","type":"codeVoice"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"여러개가 구독을 하고 있더라도 다 안내려보내준다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그러다가 completes되는 시점에 가장 마지막에 있던 거를 모든 애들한태 다 내려주고 complete을 시킨다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"BehaviorSubject","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"기본값을 가지고 시작한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"아직 데이터가 생성되지 않았을 때 누군가가 subscribe를 하자마자 기본값을 내려준다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그리고 데이터가 생기면 그때마다 계속 내려준다."}]}]},{"content":[{"inlineContent":[{"text":"새로운 게 중간에 subscribe를 하고나면 가장 최근에 발생했던 값을 일단 내려주고나서 그 다음부터 발생하는 데이터를 똑같이 모든 구독하는 애들한태 내려보내준다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"PublishSubject"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"subscribe를 하면 데이터를 그대로 내려보내준다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"다른 subscribe가 또 새롭게 subscribe 할 수 있다. 그럼 또 데이터가 생성된다면 subscribe하고 있는 모든 관찰자한태 데이터를 내려준다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"ReplaySubject"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"subscribe를 했을 때 그대로 순서대로 데이터를 내려보내준다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"두번째로 subscribe를 한다면 여태까지 발생했던 모든 데이터를 다 내려준다. 한꺼번에 Replay를 하는 것이다.","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}]},{"inlineContent":[{"type":"text","text":"내가 원했던 것은 새 이벤트가 발생했을 때에만 subscribe가 실행되었으면 했다. 따라서 새로운 이벤트만 전달받고 이전에 발생했던 이벤트는 버리는[?] PublishSubject를 선택했다."}],"type":"paragraph"},{"inlineContent":[{"text":"이후 이벤트를 발생시키기 위해 위에서 생성했던 deleted에 데이터를 전달하는 ViewModel에 메소드를 생성하였다.","type":"text"}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["func delete(_ indexPath: IndexPath, completion: ((Project?) -> Void)?) {"," useCase.delete(projects[safe: indexPath.row]) { item in"," guard let item = item else {"," self.errorMessage.onNext(\"삭제를 실패했습니다.\")"," completion?(nil)"," return"," }"," self.projects = self.useCase.fetch()"," self.deleted.onNext(indexPath)"," completion?(item)"," }"," }"]},{"type":"paragraph","inlineContent":[{"type":"text","text":"보면 인자로 받은 indexPath를 deleted에 전달하고 있는 형태이다."},{"text":" ","type":"text"},{"text":"이렇게 onNext로 새 데이터를 전달할 때마다 subscribe가 실행된다고 보면된다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"ViewController에 가서 바인딩을 해주자."}]},{"type":"codeListing","code":["class ViewController: UIViewController {",""," var viewModel = ProjectListViewModel()"," "," @IBOutlet weak var tableView: UITableView!",""," let bag = DisposeBag()"," "," override func viewDidLoad() {"," super.viewDidLoad()"," tableView.dataSource = self"," tableView.delegate = self"," bind()"," }"," "," func bind() {"," viewModel.deleted"," .subscribe(onNext: { indexPath in"," self.tableView.deleteRows(at: [indexPath], with: .fade)"," }).disposed(by: bag)"," }","\/\/ ...","}"],"syntax":"swift"},{"inlineContent":[{"type":"text","text":"전달받은 indexPath로 셀을 지울 수 있도록 deleteRows 메소드를 호출해주었다."},{"type":"text","text":" "},{"type":"text","text":"그리고 Delegate 메소드에서 delete 이벤트가 일어났을 때 ViewModel의 delete 메소드를 호출하도록 해주었다."}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["extension ViewController: UITableViewDelegate {"," func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {"," if editingStyle == .delete {"," viewModel.delete(indexPath, completion: nil)"," }"," }","}"]},{"inlineContent":[{"type":"text","text":"흐름을 다시 정리하자면…"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"셀 삭제 이벤트가 발생되면 ViewModel의 delete 메소드를 호출하여 indexPath를 전달한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"메소드 내부에서 ViewModel의 PublushSubject인 deleted에게 "},{"code":"onNext","type":"codeVoice"},{"type":"text","text":"로 "},{"code":"새 indexPath","type":"codeVoice"},{"type":"text","text":"를 전달한다."}]}]},{"content":[{"inlineContent":[{"text":"새 데이터를 전달받은 ","type":"text"},{"code":"PublushSubject","type":"codeVoice"},{"type":"text","text":"는 구독하고 있는 애들에게 이벤트가 발생했으니 subscribe를 실행하라고 알림을 준다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"바인딩해두었던 "},{"code":"subscribe","type":"codeVoice"},{"type":"text","text":"가 호출되면서 셀이 삭제된다."}]}]}]},{"type":"paragraph","inlineContent":[{"text":"이렇게 해주면 삭제 이벤트가 발생했을 때, View는 알아서 UI를 업데이트 하게 되고, ViewModel에서도 UseCase에게 데이터를 삭제요청해서 테이블뷰의 보여질 데이터도 업데이트 된다.","type":"text"}]},{"content":[{"inlineContent":[{"text":"내가 원하는 방식의 로직이 짜여졌지만, 이게 적절한 방법인지는 사실 모르겠다! 문제라면 그때가서 고쳐봐야지… 일단 PR을 보내서 리뷰를 받아봐야 알 것 같다. 어쨌든 성공해서 기쁘다~!!","type":"text"}],"type":"paragraph"}],"type":"aside","style":"note","name":"Note"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"새벽에 RxSwift 감을 익히게 도와주신 갓웨더….👍🏻"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"갓튀김님 강의!!!!!"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/www.youtube.com\/watch?v=iHKBNYMWd5I&t=8679s"}]}]}],"type":"unorderedList"}]}]}]}]}],"kind":"content"}],"kind":"article","sections":[],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Rx"]]},"schemaVersion":{"major":0,"patch":0,"minor":3},"metadata":{"title":"220304 RxSwift, Observable, Subject","modules":[{"name":"Ari's Notes"}],"role":"article","roleHeading":"Article"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220304_rxswift,_observable,_subject"]}],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/Rx":{"type":"topic","abstract":[{"type":"text","text":"RxSwift를 활용한 비동기 프로그래밍"}],"role":"collectionGroup","url":"\/documentation\/arinote\/rx","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Rx","title":"RxSwift","kind":"article"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220307_high_performance_auto_layout,_rxswift,_single,_dto.json b/docs/data/documentation/arinote/220307_high_performance_auto_layout,_rxswift,_single,_dto.json new file mode 100644 index 0000000..89373be --- /dev/null +++ b/docs/data/documentation/arinote/220307_high_performance_auto_layout,_rxswift,_single,_dto.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"content":[{"type":"heading","level":1,"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"text":"3월 7일 (월)","type":"text"}]},{"text":"학습 내용","level":2,"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Advanced Auto Layout 활동학습"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프로젝트 매니저 코드리뷰"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"RxSwift - Single"}]}]},{"content":[{"inlineContent":[{"text":"DTO","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"level":2,"text":"고민한 점 \/ 해결 방법","type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[WWDC 2018 - High Performance Auto Layout]","type":"text"}],"type":"strong"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Constraint를 추가할 때 어떤 일이 발생하는지 살펴보자"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"코드가 어떤 영향을 미칠지에 대한 감각을 얻기 위해 auto layout의 내부를 살펴보자"}],"type":"paragraph"}]}]},{"type":"aside","name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"요약"}]}],"style":"note"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"오토 레이아웃은 Engine을 통하여 방정식을 계산하고 계산된 값을 통해 레이아웃을 업데이트 한다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Engine은 layout cache이며, dependency tracker이다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이러한 과정이 Render Loop 안에 속하며, Render Loop는 1초안에 150번 발생한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"불필요한 deative, active를 하지말아야 한다. 그렇게 되면 엔진에서 계속 계산하고 없애고 계산하고를 반복하게 된다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그러므로 유동적인 오토 레이아웃을 원한다면, 정적인 것과 동적인 것을 최대한 구분하여 정적인 것은 한번만 계산하고 최소한의 변경이 필요한 것만 업데이트 하도록 한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"removeFromsuperView보다 hide가 훨씬 싼 비용이 든다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"intrinsic content size는 내부 요인을 계산하여 intrinsic content size를 계산하므로 intrinsic content size가 필요하지 않는 경우에는 override하여 성능을 향상할 수 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"system layout fitting size는 오토 레이아웃 계산 결과가 끝난 후의 frame을 반환하는데, 이는 engine을 만들고 constriant를 계산하고 engine을 삭제하는 사이클로 형성된다. 자주 호출하면 이러한 것이 자주 발생하므로 주의해야 한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"content":[{"type":"paragraph","inlineContent":[{"text":"Performance 요약","type":"text"}]}],"type":"aside","name":"Note","style":"note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"불필요한 consraint를 deallocate, allocate 하지마라"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"constraint들이 관련이 덜할수록 선형적인 시간이 된다. 단순 방정식에 계산할 뿐이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Engine은 layout cache 이고, dependency tracker이다","type":"text"}]}]}]},{"level":3,"anchor":"Constraint-churn-%ED%94%BC%ED%95%98%EA%B8%B0","text":"Constraint churn 피하기","type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"모든 Constraint를 삭제하는 것을 피한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"정적인 고정적인 constraint는 한번만 생성한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"변화가 필요한 constraint만 변경한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"View를 remove하지말고 hide하도록 하라 (물론 없어져도 다른 View에게 영향을 주지 않는 경우)"}],"type":"paragraph"}]}]},{"anchor":"Intrinsic-Content-Size","text":"Intrinsic Content Size","type":"heading","level":3},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"모든 뷰들이 갖고있는 것은 아니다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"UIImage, UILabel 같은 경우는 가지고 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UIView는 이것을 사용하여 constraint를 만들어 낸다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"UILable은 내부의 텍스트들을 고려하여 intrinsic content size를 만들어낸다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"text":"때로는 intrinsic content size를 overriden 하는게 좋을 수 있다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이렇게 되면 intrinsic content size를 이미 정해놨기 때문에 따로 text measurement 하지 않는 장점이 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"즉 intrinisic content size가 필요하지 않는 경우에 이렇게 사용하면 좋은 방법이 있다.","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"level":3,"type":"heading","anchor":"Unsatisfiable-constraint","text":"Unsatisfiable constraint"},{"items":[{"content":[{"inlineContent":[{"text":"해결책이 없고,연습이 많이 필요하고, 디버그를 잘해라.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Mysteries of Auto Layout , Part 2 - wwdc 2015를 살펴봐라","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[RxSwift - Single]","type":"text"}]}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"목적성이 있는 옵저버블","type":"text"}]},{"type":"aside","style":"note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"특징"}]}],"name":"Note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"옵저버블을 상태가 "},{"code":"onNext","type":"codeVoice"},{"text":", ","type":"text"},{"type":"codeVoice","code":"onError"},{"type":"text","text":", "},{"type":"codeVoice","code":"onCompleted"},{"text":" 등이 있는데, 애는 ","type":"text"},{"code":"success","type":"codeVoice"},{"text":" , ","type":"text"},{"code":"error","type":"codeVoice"},{"text":" 밖에없음","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"싱글은 이벤트를 한번 발행하면 끝임.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"네트워크 쪽이나 코어데이터에 사용하기 적합하다."}]}]},{"content":[{"inlineContent":[{"text":"레파지토리나 스토리지 단에서 싱글을 주로 활용함.","type":"text"}],"type":"paragraph"}]}]},{"syntax":"swift","type":"codeListing","code":["\/\/ create","func getRepo(_ repo: String) -> Single<[String: Any]> {"," return Single<[String: Any]>.create { single in"," let task = URLSession.shared.dataTask(with: URL(string: \"https:\/\/api.github.com\/repos\/\\(repo)\")!) { data, _, error in"," if let error = error {"," single(.error(error))"," return"," }",""," guard let data = data,"," let json = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves),"," let result = json as? [String: Any] else {"," single(.error(DataError.cantParseJSON))"," return"," }",""," single(.success(result))"," }",""," task.resume()",""," return Disposables.create { task.cancel() }"," }","}"]},{"type":"codeListing","syntax":"swift","code":["\/\/ subscribe","getRepo(\"ReactiveX\/RxSwift\")"," .subscribe(onSuccess: { json in"," print(\"JSON: \", json)"," },"," onError: { error in"," print(\"Error: \", error)"," })"," .disposed(by: disposeBag)"]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[DTO (Data Transfer Object)]"}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데이터 전송을 위한 객체"}]}]},{"content":[{"inlineContent":[{"text":"데이터를 오브젝트로 변환하는 객체","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"같은 시스템에서 사용되는 것이 아닌 다른 시스템으로 전달하는 작업을 처리하는 객체"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"메소드 호출 횟수를 줄이기 위해 데이터를 담고 있는 것"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"로직을 가지지 않는 순수한 객체"}]}]},{"content":[{"inlineContent":[{"text":"API에서 데이터를 받을때 받고싶은 구조대로 안올 수도 있는데, 다체적으로 원하는 형식으로 바꿀 수 있지만, 서버에서 내려주는대로 그냥 받고, 필요한 부분만 변환해서 따로 타입으로 만든다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/vapor3965.tistory.com\/20","type":"text"}]}]}]}]}]}],"kind":"content"}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220307_High_Performance_Auto_Layout,_RxSwift,_Single,_DTO","interfaceLanguage":"swift"},"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220307_high_performance_auto_layout,_rxswift,_single,_dto"]}],"sections":[],"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"220307 High Performance Auto Layout, RxSwift, Single, DTO"},"schemaVersion":{"patch":0,"major":0,"minor":3},"references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220310_oauth,_social_login,_tls,_---,_----,_---,_---.json b/docs/data/documentation/arinote/220310_oauth,_social_login,_tls,_---,_----,_---,_---.json new file mode 100644 index 0000000..f7d8cdb --- /dev/null +++ b/docs/data/documentation/arinote/220310_oauth,_social_login,_tls,_---,_----,_---,_---.json @@ -0,0 +1 @@ +{"schemaVersion":{"minor":3,"major":0,"patch":0},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220310_oauth,_social_login,_tls,_---,_----,_---,_---"]}],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220310_OAuth,_Social_Login,_TLS,_---,_----,_---,_---"},"primaryContentSections":[{"content":[{"type":"heading","level":1,"text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"type":"text","text":"3월 10일 (목)"}]},{"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"OAuth \/ Social Login 활동학습"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"level":2,"type":"heading","text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[암호화 방식]"}]}]},{"inlineContent":[{"text":"암호화 방식에는 크게 3가지 정도 있다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"대칭키"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"암호화 복호화에 동일한 키를 사용하는 방식. 그래서 키를 비공개함","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"정보 교환당사자 간에 동일한 키를 공유해야 하므로, 여러 사람과 정보 교환 시 많은 키를 유지\/관리해야 하는 어려움 존재"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"비대칭","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"암호화 복호화에 쌍을 이룬 서로 다른(공개키-개인키)를 사용하는 방식. 하나의 키는 공개"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"데이터 암호화 속도가 대칭키 암호화 방식에 비해 느리기 때문에 일반적으로 대칭키 암호화 방식의 키 분배 또는 카드번호와 같은 작은 크기의 데이터 암호화에 많이 사용된다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"해싱","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"HD5"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"만약에 abc라는 글자를 해싱을 한다고 하면 항상 123이 나오도록 하는 알고리즘"}]}]},{"content":[{"inlineContent":[{"text":"항상 일정한 값을 넣으면 일정한 출력이 나오는 알고리즘이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"암호화 대신 썼던 이유는 글자를 넣으면 임의의 글자가 나오기 때문에 유추하기 어려웠다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"하지만 해커들이 이미 테이블을 완성했기 때문에 변화된 문자열을 통해 암호를 유추할 수 있게되어서 더이상 사용하지 않는다.","type":"text"}],"type":"paragraph"}]}]}]}]},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[대칭키\/비대칭키 암호화 방식의 대표적인 알고리즘]","type":"text"}]}],"type":"paragraph"},{"level":3,"type":"heading","text":"대칭키","anchor":"%EB%8C%80%EC%B9%AD%ED%82%A4"},{"items":[{"content":[{"level":3,"type":"heading","text":"`DES`","anchor":"DES"},{"items":[{"content":[{"inlineContent":[{"identifier":"https:\/\/img1.daumcdn.net\/thumb\/R1280x0\/?scode=mtistory2&fname=https:\/\/blog.kakaocdn.net\/dn\/ddmrN1\/btrbtoJCiDF\/euecUhWqO80gmE12WMqLZ1\/img.png","type":"image"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"64bit 블록, 128bit 암호화 키 사용","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"평문을 64bit로 나눠 각 블록에 치환과 전치를 16Round 반복하여 암호화"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"heading","level":3,"text":"`3-DES`","anchor":"3-DES"},{"items":[{"content":[{"inlineContent":[{"type":"image","identifier":"https:\/\/img1.daumcdn.net\/thumb\/R1280x0\/?scode=mtistory2&fname=https:\/\/blog.kakaocdn.net\/dn\/bKuhMO\/btrbor8jqnY\/XLRYuP8fhCaGpSSOmrSfY0\/img.png"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"암호화키 2개를 사용하여 암호화(K1) -> 복호화(K2) -> 암호화(K1) 순으로 암호화","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"heading","level":3,"text":"`AES`","anchor":"AES"},{"inlineContent":[{"type":"text","text":"*"},{"identifier":"https:\/\/img1.daumcdn.net\/thumb\/R1280x0\/?scode=mtistory2&fname=https:\/\/blog.kakaocdn.net\/dn\/x60Ea\/btrbkXl5YAq\/jVNVJBMmIfOC1BQde9x05k\/img.png","type":"image"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"128bit 평문을 128bit로 암호화","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"키 크기에 따라 10\/12\/14회 Round 수행","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"라운드 키의 수 = N + 1개"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"anchor":"%EB%B9%84%EB%8C%80%EC%B9%AD%ED%82%A4","text":"비대칭키","type":"heading","level":3},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/img1.daumcdn.net\/thumb\/R1280x0\/?scode=mtistory2&fname=https:\/\/blog.kakaocdn.net\/dn\/bi9uLq\/btrbllnjBl8\/QzYfVM7KmH6EYVOVSiQs0k\/img.png"}]},{"type":"unorderedList","items":[{"content":[{"anchor":"RSA","type":"heading","text":"RSA","level":3},{"items":[{"content":[{"inlineContent":[{"text":"큰 숫자를 소인수분해하는 것이 어렵다는 것에 기반하여 개발","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"공개키만 가지고 개인키 추측 불가"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"heading","level":3,"text":"DSA","anchor":"DSA"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이산대수의 어려움을 안정성의 바탕으로 개발"}]}]}]}]}]},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Google, Apple, Naver, Kakao의 TLS 인증서는 어떤 알고리즘(들)에 의해 암(복)호화 하는지 알아보기]"}]}]},{"items":[{"content":[{"anchor":"Google%EC%9D%98-%EA%B2%BD%EC%9A%B0","level":2,"text":"Google의 경우…","type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"TLS_AES_128_GCM_SHA256"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"암호화 복호화하는 방식들의 쌍을 표현하는 방법중 하나이다. (","type":"text"},{"type":"reference","identifier":"https:\/\/ko.wikipedia.org\/wiki\/%EC%95%94%ED%98%B8%ED%99%94_%EC%8A%A4%EC%9C%84%ED%8A%B8","isActive":true},{"text":")","type":"text"}]}]}]}]}]}]}],"type":"unorderedList"},{"name":"Note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"TLS"},{"type":"text","text":" - 프로토콜(SSL\/TLS)"},{"text":" ","type":"text"},{"type":"codeVoice","code":"AES_128"},{"text":" - 대칭키를 이용한 블록 암호화 방식","type":"text"},{"text":" ","type":"text"},{"type":"codeVoice","code":"GCM"},{"type":"text","text":" - 블록 암호 운용방식"},{"type":"text","text":" "},{"code":"SHA256","type":"codeVoice"},{"text":" - 메세지 인증 (무결성)","type":"text"}]}],"style":"note"},{"inlineContent":[{"type":"text","text":"Apple, Naver, Kakao도 비슷~"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[공동인증서(구 공인인증서)는 어떤 방식으로 암(복)호화가 이뤄지는지 알아보기]"}],"type":"strong"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/2v2SXTM.png"}]},{"type":"paragraph","inlineContent":[{"text":"SEED","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"한국인터넷진흥원(KISA)에서 1999년에 개발한 대칭키 암호화 알고리즘"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"ARIA"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"한국인터넷진흥원에서 SEED의 단점을 보완하여 새롭게 낸 대칭키 암호화 알고리즘"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":"PKI(Public Key Infrastructure)","type":"text"}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/XIuN6jU.png"}]},{"style":"note","name":"Note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"text":"예전 공인인증서는 비밀키를 사용자에게 맡겨버린게 함정이다…","type":"text"},{"text":" ","type":"text"},{"text":"집에도 비밀키(인증서)를 설치하고.. 피시방에도 깔아두고.. 회사컴에도 깔아두고….. 해커도 가지고 있고…","type":"text"}]}]},{"inlineContent":[{"inlineContent":[{"text":"[암호화를 왜 알아둬야 할까?]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"직접 암호화를 구현할 일은 없겠지만… 어떤 동작으로 이루어지고 있는지, 어떤 방식으로 암호화가 되고 있는지 알고있어야 나중에 개발할 때 서버개발자와 원활한 대화를 할 수 있다.","type":"text"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[OAuth와 소셜 로그인]","type":"text"}],"type":"strong"}]},{"type":"heading","text":"OAuth란?","anchor":"OAuth%EB%9E%80","level":3},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"각종 웹, 모바일 어플리케이션에서 타사의 API를 사용하고 싶을 때 권한 획득을 위한 프로토콜(Protocol)이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자와 서버 사이에서 인증을 중개해주는 역할을 가진 메커니즘으로 제각각으로 분리된 인증방식을 통일한 표준화된 인증방식이다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"fecebook으로 로그인, Google 계정으로 로그인, Naver로 로그인"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이러한 기능으로 여러분은 특정 서비스에 대한 회원가입 과정을 거치지 않고 기존에 사용하던 서비스들의 계정으로 로그인을 진행할 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"text":"Authentication(인증)과 Authorization(권한 부여)의 차이","type":"heading","level":3,"anchor":"Authentication%EC%9D%B8%EC%A6%9D%EA%B3%BC-Authorization%EA%B6%8C%ED%95%9C-%EB%B6%80%EC%97%AC%EC%9D%98-%EC%B0%A8%EC%9D%B4"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"인증은 ‘내가 누구인가’를 증명하는 것으로 로그인과 같은 것이다"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"권한 부여는 인증을 통해 로그인한 사람이 ‘일반 사용자’인지 ‘관리자’인지 구분하는 것이다. 이 권한 부여를 통해 로그인한 사람이 접근할 수 있는 범위가 결정된다. (관리자라면 관리자 페이지 접근이 가능한 것과 같은 것)"}]}]}],"type":"unorderedList"},{"text":"OAuth 구성 요소","type":"heading","level":3,"anchor":"OAuth-%EA%B5%AC%EC%84%B1-%EC%9A%94%EC%86%8C"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Resource Owner (사용자, 브라우저 등)"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Authorization server(OAuth 인증 서버)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Resource Server(REST API)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Client(서버 = 앱)"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"RedirectURL","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"OAuth 2.0 서비스가 응용 프로그램을 승인한 후 사용자를 반환하는 곳으로 반드시 등록되어야 한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그렇지 않으면 사용자 데이터를 도용할 수 있는 악성 응용 프로그램을 쉽게 만들 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이 URL은 인증 프로세스 중 가로칠 수 있는 행위의 코드를 막는 https 엔트 포인트가 되어야 한다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"state 매개변수","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"OAuth 2.0 서비스에 불투명한 문자열이므로 초기 권한 부여 요청 시 전달되는 상태 값은 사용자가 애플리케이션을 승인한 후 반환한다.","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"heading","level":3,"anchor":"OAuth-20%EC%9D%98-%EC%9E%A5%EC%A0%90","text":"OAuth 2.0의 장점"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"타사의 정보를 통해 특정 사이트를 이용한다는 것은 매우 위험할 수 있으나 직접 타사의 아이디와 비밀번호를 입력하던 예전 방식보다 안전한 사용을 제공한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정보는 회원 정보뿐만 아니라 기타 API에 대한 정보에도 접근이 가능하다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Excess token이 server간에 교환됨으로 key가 탈취될 위험이 준다","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"유저가 A site의 계정으로 다른 B, C site들에 로그인할 수 있으므로 해당 유저는 A의 계정만 관리하면되고, B, C들은 유저의 개인정보를 못 가지게 되서 혹 B, C가 해킹 당할 경우에도 A의 개인정보를 바꿀 필요가 없다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자가 직접 B, C의 접근 권한을 취소 시킬 수 있다."}]}]}]},{"type":"paragraph","inlineContent":[{"text":"이렇게 사용자들이 타사에서 사용하고 있는 서비스들에 대한 정보를 가져와 가공하여보다 가치있는 결과물을 사용자들에게 제공할 수 있다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"가장 많이 이용되는 곳이 바로 타사의 인증된 회원정보를 통한 로그인이다."},{"text":" ","type":"text"},{"type":"text","text":"일반 로그인은 회원가입할 때 사용했던 아이디와 비밀번호를 통한 인증(Authentication)이라면 OAuth 2.0은 타사 서비스(Google, facebook)의 이메일 정보에 우리가 만든 서비스의 접근을 허락(Authorization)하여 사용자를 인증(Authentication)한다."}]},{"syntax":null,"code":["Client - 사용자가 사용하려는 우리가 만든 서비스","Resource Server - 서비스에 자신의 API를 제공하는 타사 서비스","Resource Owner - 타사 서비스 API의 정보의 주인, 즉 우리가 만든 서비스를 타사 서비스를 통해 이용하려는 사용자"],"type":"codeListing"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"text":"애플이 인정하는 암\/복호화 셋트","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/security\/1550981-ssl_cipher_suite_values?language=objc"}],"type":"paragraph"}]}]},{"inlineContent":[{"type":"text","text":"애플이 권장하는 TLS 버전"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/security\/secure_transport","type":"text"}]}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"https:\/\/kdevkr.github.io\/ssl-certificate\/","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/rsec.kr\/?p=455","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/velog.io\/@curiosity806\/HTTPS%EC%9D%98-%EC%95%94%ED%98%B8%ED%99%94SSLTLS-%EB%8C%80%EC%B9%AD%ED%82%A4-%EB%B9%84%EB%8C%80%EC%B9%AD%ED%82%A4","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/velog.io\/@inyong_pang\/Programming-%EC%95%94%ED%98%B8%ED%99%94-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EC%A2%85%EB%A5%98%EC%99%80-%EB%B6%84%EB%A5%98","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/babbab2.tistory.com\/5?category=960153","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/support.google.com\/a\/answer\/9795993?hl=ko","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}],"kind":"content"}],"kind":"article","metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"220310 OAuth, Social Login, TLS, 대칭키, 비대칭키, 암호화, 복호화"},"sections":[],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/XIuN6jU.png":{"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/XIuN6jU.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/XIuN6jU.png","alt":null},"https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https://blog.kakaocdn.net/dn/bi9uLq/btrbllnjBl8/QzYfVM7KmH6EYVOVSiQs0k/img.png":{"alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/img1.daumcdn.net\/thumb\/R1280x0\/?scode=mtistory2&fname=https:\/\/blog.kakaocdn.net\/dn\/bi9uLq\/btrbllnjBl8\/QzYfVM7KmH6EYVOVSiQs0k\/img.png"}],"identifier":"https:\/\/img1.daumcdn.net\/thumb\/R1280x0\/?scode=mtistory2&fname=https:\/\/blog.kakaocdn.net\/dn\/bi9uLq\/btrbllnjBl8\/QzYfVM7KmH6EYVOVSiQs0k\/img.png","type":"image"},"https://i.imgur.com/2v2SXTM.png":{"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/2v2SXTM.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/2v2SXTM.png","alt":null},"https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https://blog.kakaocdn.net/dn/x60Ea/btrbkXl5YAq/jVNVJBMmIfOC1BQde9x05k/img.png":{"alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/img1.daumcdn.net\/thumb\/R1280x0\/?scode=mtistory2&fname=https:\/\/blog.kakaocdn.net\/dn\/x60Ea\/btrbkXl5YAq\/jVNVJBMmIfOC1BQde9x05k\/img.png"}],"identifier":"https:\/\/img1.daumcdn.net\/thumb\/R1280x0\/?scode=mtistory2&fname=https:\/\/blog.kakaocdn.net\/dn\/x60Ea\/btrbkXl5YAq\/jVNVJBMmIfOC1BQde9x05k\/img.png","type":"image"},"https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https://blog.kakaocdn.net/dn/bKuhMO/btrbor8jqnY/XLRYuP8fhCaGpSSOmrSfY0/img.png":{"type":"image","variants":[{"url":"https:\/\/img1.daumcdn.net\/thumb\/R1280x0\/?scode=mtistory2&fname=https:\/\/blog.kakaocdn.net\/dn\/bKuhMO\/btrbor8jqnY\/XLRYuP8fhCaGpSSOmrSfY0\/img.png","traits":["1x","light"]}],"identifier":"https:\/\/img1.daumcdn.net\/thumb\/R1280x0\/?scode=mtistory2&fname=https:\/\/blog.kakaocdn.net\/dn\/bKuhMO\/btrbor8jqnY\/XLRYuP8fhCaGpSSOmrSfY0\/img.png","alt":null},"https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https://blog.kakaocdn.net/dn/ddmrN1/btrbtoJCiDF/euecUhWqO80gmE12WMqLZ1/img.png":{"alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/img1.daumcdn.net\/thumb\/R1280x0\/?scode=mtistory2&fname=https:\/\/blog.kakaocdn.net\/dn\/ddmrN1\/btrbtoJCiDF\/euecUhWqO80gmE12WMqLZ1\/img.png"}],"identifier":"https:\/\/img1.daumcdn.net\/thumb\/R1280x0\/?scode=mtistory2&fname=https:\/\/blog.kakaocdn.net\/dn\/ddmrN1\/btrbtoJCiDF\/euecUhWqO80gmE12WMqLZ1\/img.png","type":"image"},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]},"https://ko.wikipedia.org/wiki/%EC%95%94%ED%98%B8%ED%99%94_%EC%8A%A4%EC%9C%84%ED%8A%B8":{"title":"Cipher Suite","titleInlineContent":[{"type":"text","text":"Cipher Suite"}],"url":"https:\/\/ko.wikipedia.org\/wiki\/%EC%95%94%ED%98%B8%ED%99%94_%EC%8A%A4%EC%9C%84%ED%8A%B8","identifier":"https:\/\/ko.wikipedia.org\/wiki\/%EC%95%94%ED%98%B8%ED%99%94_%EC%8A%A4%EC%9C%84%ED%8A%B8","type":"link"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220314_mvvm,_rx,_changed,_coordinator,_observable.json b/docs/data/documentation/arinote/220314_mvvm,_rx,_changed,_coordinator,_observable.json new file mode 100644 index 0000000..77d6b53 --- /dev/null +++ b/docs/data/documentation/arinote/220314_mvvm,_rx,_changed,_coordinator,_observable.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Rx"]]},"variants":[{"paths":["\/documentation\/arinote\/220314_mvvm,_rx,_changed,_coordinator,_observable"],"traits":[{"interfaceLanguage":"swift"}]}],"primaryContentSections":[{"content":[{"type":"heading","text":"TIL (Today I Learned)","level":1,"anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"type":"text","text":"3월 14일 (월)"}]},{"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"text":"학습 내용"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"MVVM 활동학습"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"RxCocoa - changed"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"escaping closure를 RxSwift로 리팩토링해보기","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Coordinator 패턴이란?","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","type":"heading","level":2},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[3월 14일 퀴즈 오답노트]"}]}]},{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"1초당 120프레임으로 실행되는 프로세스이며 Constranints, Layout, Display의 3개 Phase로 구성되는 것은?"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Render Loop","type":"text"}]}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/62imUsh.png"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"code":"객체지향 프로그래밍 OOP의 4대 특징","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"캡슐화, 상속, 다형성, 추상화","type":"text"}]}]}]},{"inlineContent":[{"type":"codeVoice","code":"Render Loop의 다음 update Cycle에서 Constraints에 대한 갱신이 일괄적으로 일어나도록 변경사항을 예약하는 메서드는?"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"setNeedsUpdateConstraints","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"다음 업데이트 사이클에서 제약조건에 대한 갱신이 일괄적으로 일어나도록 변경사항을 예약한다"}]}]}]}]}],"type":"unorderedList"},{"type":"aside","name":"Note","content":[{"inlineContent":[{"text":"실제로 업데이트 실행(재정의할 뿐 직접 호출하면 안된다!)","type":"text"}],"type":"paragraph"}],"style":"note"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"updateConstraints","type":"text"}]}]}],"type":"unorderedList"},{"name":"Note","content":[{"inlineContent":[{"text":"다음 업데이트 주기에 업데이트가 필요한 것으로 명시적으로 표시","type":"text"}],"type":"paragraph"}],"style":"note","type":"aside"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"setNeedsUpdateConstraints"}]}]}]},{"name":"Note","style":"note","content":[{"type":"paragraph","inlineContent":[{"text":"업데이트가 필요하면 즉시 업데이트","type":"text"}]}],"type":"aside"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"updateConstraintsIfNeeded"}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"codeVoice","code":"현재 많이 사용되고있는 HTTP의 버전은?"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"1.1 버전"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"1997년 1월에 최초 개발된 표준 프로토콜이며 현재도 많이 사용하고 있다."}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"code":"자식 클래스에서 프로퍼티를 override 하려고하는데, 에러나지 않는 구문은?","type":"codeVoice"}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/opM0lfq.jpg"}],"type":"paragraph"},{"inlineContent":[{"type":"codeVoice","code":"재귀함수를 주의해서 사용하지 못하면 스택 오버 플로우가 발생한다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"함수는 스택영역에서 실행되는데 재귀함수는 자기자신을 반복적으로 호출하게 된다. 따라서 반복적인 함수 호출로 인한 스택 오버 플로우가 발생한다."}]}]}]},{"inlineContent":[{"code":"async await 구문을 올바르게 사용하는 방법","type":"codeVoice"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/sCmEzYG.jpg"}]},{"type":"orderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"async"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"throws"}]}]},{"content":[{"inlineContent":[{"text":"try","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"await"}],"type":"paragraph"}]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/bCi5eRX.png","type":"image"}],"type":"paragraph"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[웨더와 함께하는 MVVM 실습!]","type":"text"}]}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"ViewModel의 경우 UIKit을 import 하지 않는 것이 중요하다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["import Foundation","","class YagomViewModel {"," enum Color {"," case red"," case blue"," }"," private(set) var currentBackgroundColor: Color? {"," didSet {"," listener?()"," }"," }"," private(set) var data: [String] = [] {"," didSet {"," reloadData?()"," }"," }"," "," private var listener: (() -> Void)?"," private var reloadData: (() -> Void)?",""," "," func bind(_ closure: @escaping () -> Void) {"," listener = closure"," }"," "," func reloadDataBind(_ closure: @escaping () -> Void) {"," reloadData = closure"," }"," "," func didTapRedButton() {"," changedColor(color: .red)"," }"," "," func didTapblueButton() {"," changedColor(color: .blue)"," }"," "," func didTapApiButton() {"," DispatchQueue.main.asyncAfter(deadline: .now() + 1) {"," self.data.append(contentsOf: [\"야\", \"곰\", \"아\", \"카\", \"데\", \"미\"])"," }"," }"," "," private func changedColor(color: Color) {"," currentBackgroundColor = color"," }","}"]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"View는 ViewModel에게 이벤트만을 전달한다.","type":"text"}]}]}]},{"code":["import UIKit","","class ViewController: UIViewController {",""," @IBOutlet weak var redButton: UIButton!"," @IBOutlet weak var blueButton: UIButton!"," @IBOutlet weak var apiButton: UIButton!"," @IBOutlet private weak var tableView: UITableView!"," "," var viewModel = YagomViewModel()"," override func viewDidLoad() {"," super.viewDidLoad()"," setUpBindings()"," tableView.dataSource = self"," }"," "," \/\/ 뷰모델과 뷰를 바인딩"," private func setUpBindings() {"," viewModel.bind { [weak self] in"," self?.view.backgroundColor = self?.viewModel.currentBackgroundColor == .red ? .systemRed : .systemBlue"," }"," viewModel.reloadDataBind { [weak self] in"," self?.tableView.reloadData()"," }"," }",""," \/\/ 뷰모델에게 이벤트만 전달~"," @IBAction func didTapRedButton(_ sender: UIButton) {"," viewModel.didTapRedButton()"," }",""," @IBAction func didTapblueButton(_ sender: UIButton) {"," viewModel.didTapblueButton()"," }"," "," @IBAction func didTapApiButton(_ sender: UIButton) {"," viewModel.didTapApiButton()"," }","}","","extension ViewController: UITableViewDataSource {"," func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {"," guard let cell = tableView.dequeueReusableCell(withIdentifier: \"cell\") else {"," return UITableViewCell()"," }"," "," cell.textLabel?.text = viewModel.data[indexPath.row]"," return cell"," }"," "," func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {"," return viewModel.data.count"," }","}"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"내 입맛대로 설계해본 MVVM 코드…"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"웨더는 Delegate 패턴으로 구현해주셨지만 나는 Observable이 편해서 Observable 형태로 구현해보았다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"데이터를 관찰하면서, 변화할 때마다 특정 클로저를 실행해줄 프로퍼티 옵저버("},{"type":"codeVoice","code":"currentBackgroundColor"},{"type":"text","text":", "},{"type":"codeVoice","code":"data"},{"type":"text","text":")를 생성하고, 해당 클로저에 값을 할당해줄 메소드("},{"code":"bind","type":"codeVoice"},{"text":", ","type":"text"},{"type":"codeVoice","code":"reloadDataBind"},{"type":"text","text":")를 만들어주었다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"ViewController에서 ViewModel을 생성하고, 바인딩 작업을 해주었다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컬러가 바뀔 경우 배경색을 바꾸도록 하고, 배열이 변화할 때마다 reloadData() 메소드를 실행하도록 바인딩 해주었다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이후 각 버튼마다 ViewModel에게 이벤트를 전달하도록 ViewModel의 메소드를 호출해주었다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"TableView는 ViewModel의 data라는 배열로 구성해주었다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이후 실행하면 각 버튼을 누를때, ViewModel에게 이벤트가 전달되고, ViewModel에서는 값을 변경한다. 변경되면 didSet에 등록되어있는 클로저가 실행된다. 해당 클로저는 ViewController에서 바인딩 처리해준 작업들이다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"aside","name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"웨더 QnA","type":"text"}]}],"style":"note"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"MVVM을 잘~쓰면 좋다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"신입들 대부분 MVVM을 제대로 쓰지 못한다. MVVM을 왜쓰는지 알아보고 이유를 갖고 활용하자."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그리고 ViewModel이 ViewModel스럽게 올바른 역할을 하고있는지 중요하다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테스트가 가능하도록 역할을 잘 분리하는 것도 중요"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"클린 아키텍처…내용","type":"text"}]}]}]}]}]},{"style":"note","content":[{"inlineContent":[{"text":"느낀점","type":"text"}],"type":"paragraph"}],"type":"aside","name":"Note"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"너무 어렵지만… 계속 반복하다보면 언젠가 깨달음이 오겠지…?"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"클린 아키텍처는 아직도 어려워…","type":"text"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[Coordinator 패턴]","type":"text"}]}],"type":"paragraph"},{"level":3,"anchor":"Coordinator%EB%9E%80","type":"heading","text":"Coordinator란?"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"하나 이상의 뷰 컨트롤러들에게 지시를 내리는 객체이며, 여기서 말하는 지시는 View의 트랜지션을 의미한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"즉, Coordinator는 앱 전반에 있어 화면 전환 및 계층에 대한 흐름을 제어하는 역할을 한다.","type":"text"}],"type":"paragraph"}]}]},{"level":3,"anchor":"%EC%88%98%ED%96%89%EA%B8%B0%EB%8A%A5","text":"수행기능","type":"heading"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"화면 전환에 필요한 인스턴스 생성(ViewController, ViewModel …)"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"생성한 인스턴스의 종속성 주입(DI)"}]}]},{"content":[{"inlineContent":[{"text":"생성된 ViewController의 화면 전환","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"level":3,"text":"왜 사용할까?","type":"heading","anchor":"%EC%99%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ViewController가 담당하던 화면 전환 책임을 Coordinator가 담당하게되면서, 화면전환 시 ViewController에서 사용할 ViewModel을 함께 주입해줄 수 있다."}]}]},{"content":[{"inlineContent":[{"text":"또한 화면 전환에 대한 코드를 따로 관리하게 되면서 재사용과 유지보수를 편하게 만들어주기 때문에 주로 사용한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"정리하자면 Coordinator는 화면 전환 제어 담당과 의존성 주입을 가능하게 해주는 허브라고 생각하면 될 것 같다."}],"type":"paragraph"}]}]},{"name":"Note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"text":"느낀점","type":"text"}]}],"style":"note"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"아 일단 만들어보긴 했는데… 제대로 만든건지 모르겠다… 왤케 어렵지"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그래도 만들고나니까 화면전환을 viewModel 단에서 해결할 수 있어 개쉬워짐.","type":"text"}]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[UIAlertController를 Rx스럽게 리팩토링 해보기]","type":"text"}]}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["func showActionSheet("," sourceView: UIView,"," titles: (String, String),"," topHandler: @escaping (UIAlertAction) -> Void,"," bottomHandler: @escaping (UIAlertAction) -> Void",") {"," let topAction = UIAlertAction(title: \"Move to \\(titles.0)\", style: .default, handler: topHandler)"," let bottomAction = UIAlertAction(title: \"Move to \\(titles.1)\", style: .default, handler: bottomHandler)"," let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)"," alert.addAction(topAction)"," alert.addAction(bottomAction)"," if let popoverController = alert.popoverPresentationController {"," popoverController.sourceView = sourceView"," let rect = CGRect(x: .zero, y: .zero, width: sourceView.bounds.width, height: sourceView.bounds.height \/ 2)"," popoverController.sourceRect = rect"," popoverController.permittedArrowDirections = [.up, .down]"," }"," navigationController.topViewController?.present(alert, animated: true)","}"]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"라이언한태 코드리뷰 받고난 후 escaping 클로저만 보면… ‘아 옵저버블 쓸 수 있을 거 같은데?’ 라는 생각에 빠진다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"오늘도 어김없이 옵저버블을 쓸 수 있을 것 같아서 찾아보니까… 예제코드들이 많길래 도전해보았다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 위 코드를 아래와 같이 수정해보았다."}],"type":"paragraph"}]}]},{"type":"codeListing","syntax":"swift","code":["enum ActionType: CaseIterable {"," case top"," case bottom","}","","func showActionSheet(sourceView: UIView, titles: [String]) -> Observable {"," return Observable.create { observer in"," let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)"," ActionType.allCases.enumerated().forEach { index, _ in"," let action = UIAlertAction(title: \"Move to \\(titles[index])\", style: .default) { _ in"," observer.onNext(ProjectState(rawValue: titles[index]) ?? ProjectState.todo)"," observer.onCompleted()"," }"," alert.addAction(action)"," }"," if let popoverController = alert.popoverPresentationController {"," popoverController.sourceView = sourceView"," let rect = CGRect("," x: .zero,"," y: .zero,"," width: sourceView.bounds.width,"," height: sourceView.bounds.height \/ 2"," )"," popoverController.sourceRect = rect"," popoverController.permittedArrowDirections = [.up, .down]"," }"," self.navigationController.topViewController?.present(alert, animated: true)",""," return Disposables.create {"," alert.dismiss(animated: true, completion: nil)"," }"," }","}"]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"뭐가 많이 바뀐 것 같지만… 별거없다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ActionType이라는 enum을 만들고 해당 케이스를 반복하면서 핸들러 내부에 onNext로 ProjectState라는 데이터와 함께 이벤트를 전달해준다."}]}]},{"content":[{"inlineContent":[{"text":"나머지는 iPad를 위한 popover 설정…","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"codeListing","code":["showActionSheet(sourceView: cell, titles: project.status.excluded)"," .subscribe(onNext: { state in"," self.useCase.changedState(project, state: state)"," }).disposed(by: disposeBag)"],"syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"사용할 때(구독)는 onNext로 전달받은 state값으로 project의 상태값을 바꿔주는 작업을 해주었다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이때 파라미터로 sourceView를 넘겨주는 이유는 popover를 띄울 위치를 잡기 위함인데… ViewModel에 UIKit을 import해야해서 몹시 불편하다.."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"이부분은 고민해보았지만 좋은 방법이 떠오르지가 않아서 개선하지 못했다.","type":"text"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[UI의 value가 변경되었을 때만 이벤트 받기]","type":"text"}],"type":"strong"}]},{"syntax":"swift","code":["let input = DetailViewModel.Input("," didTapRightBarButton: rightBarButton.rx.tap.asObservable(),"," didTapLeftBarButton: leftBarButton.rx.tap.asObservable(),"," didChangeTitleText: titleTextField.rx.text.asObservable(),"," didChangeDatePicker: datePicker.rx.date.asObservable(),"," didChangeDescription: descriptionTextView.rx.text.asObservable())"," didChangeTitleText: titleTextField.rx.text.changed.asObservable(),"," didChangeDatePicker: datePicker.rx.date.changed.asObservable(),"," didChangeDescription: descriptionTextView.rx.text.changed.asObservable()",")"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"처음엔 위와 같이 단순하게 input을 만들어주었는데…"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이렇게 만들다보니 TextField의 경우 값을 수정하지 않고 tap해서 활성화만 해도 이벤트를 전달받는 것을 확인했다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"이러면 값을 변경하지 않고 modal을 닫아도, 이벤트를 받고 값이 수정된 것 마냥 빈문자열이 들어와서 기존 데이터가 사라지는… 버그가 발생했다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"아무것도 안해도.. Modal만 띄우고 닫아도.. 빈문자열 이벤트를 받아서 데이터가 지워지는….🥲"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"구글링을 해보니 changed라는 ControlProperty를 찾게 되었고, 아래와 같이 값이 변경될때 마다 이벤트를 전달하는 옵저버블로 변경해주었다"}]}]}]},{"code":["let input = DetailViewModel.Input("," didTapRightBarButton: rightBarButton.rx.tap.asObservable(),"," didTapLeftBarButton: leftBarButton.rx.tap.asObservable(),"," didChangeTitleText: titleTextField.rx.text.changed.asObservable(), \/\/ changed"," didChangeDatePicker: datePicker.rx.date.changed.asObservable(), \/\/ changed"," didChangeDescription: descriptionTextView.rx.text.changed.asObservable() \/\/ changed",")"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"그리고 output을 설정해줄때 논옵셔널 타입으로 설정해주었는데, 옵셔널 타입으로 바꿔주고, nil일 경우 기존 데이터를 전달해서, 값이 임의로 변경되지 않도록 처리해주었다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이렇게 하니까 값을 수정하지 않으면 정상적으로 수정되지 않았고, 해당 문제를 해결할 수 있었다."}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@aurora_97\/RxSwift-UIAlertController"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/velog.io\/@dvhuni\/UITextField%EC%97%90%EC%84%9C-rx.text%EB%A1%9C-%EB%B3%80%EA%B2%BD%EB%90%9C-%ED%85%8D%EC%8A%A4%ED%8A%B8%EB%A5%BC-%EA%B0%90%EC%A7%80%ED%95%98%EA%B8%B0"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/duwjdtn11.tistory.com\/644"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/jintaewoo.tistory.com\/58"}]}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"sections":[],"metadata":{"title":"220314 MVVM, Rx, changed, coordinator, observable","roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}]},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220314_MVVM,_Rx,_changed,_coordinator,_observable","interfaceLanguage":"swift"},"schemaVersion":{"patch":0,"minor":3,"major":0},"kind":"article","references":{"doc://AriNote/documentation/AriNote/Rx":{"type":"topic","abstract":[{"type":"text","text":"RxSwift를 활용한 비동기 프로그래밍"}],"role":"collectionGroup","url":"\/documentation\/arinote\/rx","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Rx","title":"RxSwift","kind":"article"},"https://i.imgur.com/opM0lfq.jpg":{"alt":null,"identifier":"https:\/\/i.imgur.com\/opM0lfq.jpg","variants":[{"url":"https:\/\/i.imgur.com\/opM0lfq.jpg","traits":["1x","light"]}],"type":"image"},"https://i.imgur.com/bCi5eRX.png":{"alt":null,"identifier":"https:\/\/i.imgur.com\/bCi5eRX.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/bCi5eRX.png"}],"type":"image"},"https://i.imgur.com/62imUsh.png":{"alt":null,"identifier":"https:\/\/i.imgur.com\/62imUsh.png","variants":[{"url":"https:\/\/i.imgur.com\/62imUsh.png","traits":["1x","light"]}],"type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/sCmEzYG.jpg":{"alt":null,"identifier":"https:\/\/i.imgur.com\/sCmEzYG.jpg","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/sCmEzYG.jpg"}],"type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220315_memory_leak,_rxswift,_withunretained,_widget.json b/docs/data/documentation/arinote/220315_memory_leak,_rxswift,_withunretained,_widget.json new file mode 100644 index 0000000..32cb597 --- /dev/null +++ b/docs/data/documentation/arinote/220315_memory_leak,_rxswift,_withunretained,_widget.json @@ -0,0 +1 @@ +{"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220315_Memory_Leak,_RxSwift,_withUnretained,_Widget"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/SwiftUI"]]},"primaryContentSections":[{"kind":"content","content":[{"level":1,"text":"TIL (Today I Learned)","type":"heading","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"type":"text","text":"3월 15일 (화)"}]},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading","text":"학습 내용","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"modal이 닫혀도 사라지지 않는… memory leak 해결하기"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"RxSwift - withUnretained()"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Widget 예습 (3\/16)"}],"type":"paragraph"}]}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"text":"고민한 점 \/ 해결 방법","level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","type":"heading"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[Memory Leak 확인하는 방법]","type":"text"}],"type":"strong"}]},{"type":"aside","style":"note","content":[{"inlineContent":[{"type":"text","text":"modal을 Cancel 버튼이 아니라 다른 View를 터치해서 창을 내릴 경우 메모리에서 사라지지 않고 메모리가 계속 늘어나는 것을 확인했다. 정확히 메모리 누수가 발생하는 것인지 궁금하여 찾다가 "},{"code":"Instrumnets","type":"codeVoice"},{"type":"text","text":"라는 도구를 알게되었다."}],"type":"paragraph"}],"name":"Note"},{"items":[{"content":[{"inlineContent":[{"text":"메모리 누수가 되고있는지 확인하려면 ","type":"text"},{"type":"codeVoice","code":"Command + I"},{"type":"text","text":"를 눌러 빌드를 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"그러면 Instrumnets 도구가 뜨는데…"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Instrumnets란?"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Xcode에 통합된 일련의 애플리케이션 성능 분석 도구"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Allocation 상태를 확인 가능"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Memory leak 상태 확인 가능"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/thRuFvQ.png"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"도구가 뜨면 여러 아이콘 중에서 ","type":"text"},{"code":"Allocations","type":"codeVoice"},{"type":"text","text":"라는 아이콘을 클릭하면,"}]}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/QAUgfyN.png"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"위와 같은 창이 나타난다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"여기서 좌측에 빨간색 녹화버튼을 누르면 시뮬레이터가 실행되면서 수치를 기록해준다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/YWf4Ejj.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"메모리 누수가 발생할 경우 아래처럼 메모리 카운트가 올라간다."}]}]}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/GbcKLb2.png"}],"type":"paragraph"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[RxSwift - withUnretained()]","type":"text"}]}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"보통은 클로저 내부에서 강한 참조 사이클을 방지하기 위해 weak self와 guard let self를 활용하여 바인딩 처리를 해주는데, 이 동작을 간결하게 해주는 operator가 존재했다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"RxSwift 6.0부터 새롭게 생겼으며, weak self 대신 활용할 수 있다."}]}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["viewModel.someInfo \/\/ Observable"," .withUnretained(self) \/\/ (self, String) 튜플로 변환해줌"," .bind { (owner, string) in"," owner.label.text = string \/\/ owner를 self 대신 사용!"," }"," .disposed(by: disposeBag)"]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[Widget 만들기]","type":"text"}],"type":"strong"}]},{"type":"heading","level":3,"anchor":"%EC%8B%9C%EC%9E%91%EC%A0%84","text":"시작전"},{"inlineContent":[{"type":"text","text":"위젯은 기능이 제한적이며 interactive 하지도 않지만 우리는 앱과 위젯이 데이터를 공유하기를 원할수도 있다."}],"type":"paragraph"},{"style":"note","type":"aside","content":[{"inlineContent":[{"text":"App과 Extension 간의 관계","type":"text"},{"text":" ","type":"text"},{"identifier":"https:\/\/developer.apple.com\/library\/archive\/documentation\/General\/Conceptual\/ExtensibilityPG\/ExtensionScenarios.html#\/\/apple_ref\/doc\/uid\/TP40014214-CH21-SW1","type":"reference","isActive":true},{"type":"text","text":" 를 살펴보면 App 과 Extension 간의 관계를 볼 수 있다."}],"type":"paragraph"}],"name":"Note"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/BzmcpTf.png","type":"image"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"extension’s bundle이 containing app’s bundle내에 중첩되더라도 실행중인 app extension과 containg app은 서로의 contatiner에 접근할 수 없다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"하지만 데이터 공유를 활성화 할 수 있다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"container app과 contained app extensions의 App Groups를 활성화하고 앱에서 사용할 App Groups를 지정한다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"App Groups를 활성화하면 app extension과 containing app 모두 UserDefaults를 사용해서 데이터를 공유할 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그 후 새로운 UserDefaults 객체를 인스턴스화하고 App Groups의 식별자를 전달하면 된다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"level":3,"anchor":"%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EC%84%A4%EC%A0%95-App-Groups-%EA%B6%8C%ED%95%9C","text":"프로젝트 설정 - App Groups 권한","type":"heading"},{"content":[{"type":"paragraph","inlineContent":[{"text":"우선 데이터를 공유하려면 App Groups에 추가해야한다.","type":"text"}]}],"style":"note","type":"aside","name":"Note"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"code":"main app Target","type":"codeVoice"},{"type":"text","text":" > "},{"type":"codeVoice","code":"Signing & Capabilities"},{"text":" > ","type":"text"},{"type":"codeVoice","code":"+ Capability"},{"type":"text","text":" > "},{"type":"codeVoice","code":"App Groups 추가"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"New Container"},{"text":"를 추가한다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이때 "},{"code":"group.","type":"codeVoice"},{"type":"text","text":"을 prefix로 가지는 포맷이 제공되는데, App Group identifier는 bundle identifier처럼 유니크한 값이기 때문에 번들을 활용하여 이름을 지어주었다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ex) "},{"type":"codeVoice","code":"group.com.leeari.net.widget-example.My-Widget"}]}]}],"type":"unorderedList"}]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/DJHnOin.png","type":"image"}],"type":"paragraph"},{"level":3,"type":"heading","text":"앱에 Widget Target을 추가하기","anchor":"%EC%95%B1%EC%97%90-Widget-Target%EC%9D%84-%EC%B6%94%EA%B0%80%ED%95%98%EA%B8%B0"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/qyXbFQd.png"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Xcode에서 프로젝트를 열고 ","type":"text"},{"type":"codeVoice","code":"File"},{"type":"text","text":" > "},{"type":"codeVoice","code":"New"},{"text":" > ","type":"text"},{"code":"Target","type":"codeVoice"},{"type":"text","text":"을 클릭한다."}]}]},{"content":[{"inlineContent":[{"text":"Application Extension 그룹에서 ","type":"text"},{"type":"codeVoice","code":"Widget Extension"},{"text":"을 클릭한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"위젯 이름을 입력하고 create를 해주면 끝~","type":"text"}]}]}],"type":"unorderedList"},{"level":3,"anchor":"WidgetConfiguration","type":"heading","text":"WidgetConfiguration"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"IntentConfiguration"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"사용자가 구성할 수 있는 속성이 있는 위젯","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"위젯에서 Edit을 통해서 위젯에 보여질 내용을 변경할 수 있다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"StaticConfiguration"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"사용자가 구성할 수 있는 속성이 없는 위젯","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"정적인 데이터를 보여주기에 알맞은 타입의 위젯이다.","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"일반적으로 주식시장 위젯이나 뉴스 헤드라인을 보여주는 위젯"}]}]}]}]}]}]}]},{"type":"aside","name":"Note","style":"note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 구성들의 이니셜라이저 파라미터를 하나씩 살펴보자."}]}]},{"code":["@main","struct MyWidget: Widget {"," private let kind: String = \"My_Widget\"",""," var body: some WidgetConfiguration { \/\/ 위젯 식별 및 위젯의 Content 표시"," StaticConfiguration( \/\/ 정적인 데이터를 보여주기에 알맞은 타입의 위젯"," kind: kind, \/\/ 위젯의 identifier"," provider: Provider() \/\/ 렌더링할 시기를 widgetkit에 알려주는 타임라인을 생성"," ) { entry in"," WidgetEntryView(entry: entry)"," }"," .supportedFamilies([.systemLarge])"," }","}"],"type":"codeListing","syntax":"swift"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Kind"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"위젯을 식별하는 문자열","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Provider"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"TimelineProvider를 준수하고 위젯을 렌더링할 시기를 WidgetKit에 알려주는 타임라인을 생성해준다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"타임라인에는 사용자가 정의한 Custom TimelineEntry 타입이 포함되어있다."}]}]},{"content":[{"inlineContent":[{"text":"TimelineEntry은 WidgetKit이 위젯의 콘텐츠를 업데이트하기를 원하는 date를 식별한다.","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"syntax":"swift","type":"codeListing","code":["struct Provider: TimelineProvider { \/\/ 시간에 따른 위젯 업데이트 로직"," @AppStorage(\"emoji\", store: UserDefaults(suiteName: \"group.com.leeari.net.widget-example\"))"," var emojiData: Data = Data()",""," \/\/ 특정 내용이 없는 시각적 표현"," func placeholder(in context: Context) -> EmojiEntry {"," EmojiEntry(emoji: Emoji(icon: \"😶‍🌫️\", name: \"N\/A\", description: \"N\/A\"))"," }"," "," \/\/ 위젯 갤러리에서 보여질 부분"," func getSnapshot(in context: Context, completion: @escaping (EmojiEntry) -> Void) {"," guard let emoji = try? JSONDecoder().decode(Emoji.self, from: emojiData) else {"," return"," }"," let entry = EmojiEntry(emoji: emoji)"," completion(entry)"," }"," "," \/\/ 정의한 타임라인에 맞게 업데이트해서 보여질 내용"," func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) {"," guard let emoji = try? JSONDecoder().decode(Emoji.self, from: emojiData) else {"," return"," }"," let entry = EmojiEntry(emoji: emoji)"," let timeline = Timeline(entries: [entry], policy: .atEnd) \/\/ 타임라인을 제공해주는 시기를 가능한 즉시 새로운 타임라인을 요청"," completion(timeline)"," }"," "," ","}",""]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Content Closure"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"SwiftUI 뷰를 포함하는 클로저"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"WidgetKit은 이를 호출하여 위젯의 콘텐츠를 렌더링하고 Provider로부터 TimelineEntry 파라미터를 전달한다."}]}]}]}]}],"type":"unorderedList"},{"type":"codeListing","syntax":"swift","code":["struct WidgetEntryView : View { \/\/ 위젯을 표시하는 뷰"," var entry: Provider.Entry",""," @Environment(\\.widgetFamily) var family \/\/ 위젯 크기를 다양하게 접근할 수 있는 래퍼"," "," @ViewBuilder"," var body: some View {"," switch family { \/\/ 여러 사이즈에 대응하는 로직"," case .systemSmall:"," EmojiView(emoji: entry.emoji)"," case .systemMedium:"," HStack(spacing: 30) {"," EmojiView(emoji: entry.emoji)"," Text(entry.emoji.name)"," .font(.largeTitle)"," }"," default:"," VStack(spacing: 30) {"," HStack(spacing: 30) {"," EmojiView(emoji: entry.emoji)"," Text(entry.emoji.name)"," .font(.largeTitle)"," }"," Text(entry.emoji.description)"," .font(.title2)"," .padding()"," }"," }"," EmojiView(emoji: entry.emoji)"," }","}"]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"code":"Custom Intent","type":"codeVoice"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Custom 가능한 속성을 정의하는 user configurable 프로퍼티"}]}]}],"type":"unorderedList"}]}]},{"text":"Widget Modifier","level":3,"anchor":"Widget-Modifier","type":"heading"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/r5iCn6o.png"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"Configuration 밑에 modifier들이 붙어있는 것을 볼 수 있는데, 간단히 3가지 정도가 있다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"configurationDisplayName","type":"codeVoice"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"사용자가 위젯을 추가\/편집할 때 위젯에 표시되는 이름을 설정하는 메소드"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"description"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자가 위젯을 추가\/편집할 때 위젯에 표시되는 설명을 설정하는 메소드"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"supportedFamilies"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"위젯이 지원하는 크기를 설정할 수 있는 메소드"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"배열에 하나만 넣어주면 하나의 사이즈만 나오게되고, 별다른 설정을 해주지 않는다면 3가지 사이즈가 기본으로 설정된다."}]}]}]}]}],"type":"unorderedList"},{"level":3,"type":"heading","text":"TimelineEntry","anchor":"TimelineEntry"},{"syntax":"swift","type":"codeListing","code":["struct EmojiEntry: TimelineEntry {"," var date: Date = Date()"," let emoji: Emoji","}"]},{"inlineContent":[{"type":"text","text":"TimelineEntry는 프로토콜인데, date라는 프로퍼티를 필수적으로 요구한다."},{"type":"text","text":" "},{"text":"이 date는 WidgetKit이 widget을 렌더링할 날짜를 의미한다.","type":"text"},{"type":"text","text":" "},{"text":"Timleline은 위젯을 만들 때 Widget을 어떤 시점에 업데이트할지 알려줄 때 사용한다.","type":"text"},{"type":"text","text":" "},{"text":"Provider의 getTimeline 메소드가 해당 역할을 한다.","type":"text"}],"type":"paragraph"},{"text":"TimelineProvider","anchor":"TimelineProvider","type":"heading","level":3},{"syntax":"swift","type":"codeListing","code":["struct Provider: TimelineProvider { \/\/ 시간에 따른 위젯 업데이트 로직"," @AppStorage(\"emoji\", store: UserDefaults(suiteName: \"group.com.leeari.net.widget-example\"))"," var emojiData: Data = Data()",""," \/\/ 특정 내용이 없는 시각적 표현"," func placeholder(in context: Context) -> EmojiEntry {"," EmojiEntry(emoji: Emoji(icon: \"😶‍🌫️\", name: \"N\/A\", description: \"N\/A\"))"," }"," "," \/\/ 위젯 갤러리에서 보여질 부분"," func getSnapshot(in context: Context, completion: @escaping (EmojiEntry) -> Void) {"," guard let emoji = try? JSONDecoder().decode(Emoji.self, from: emojiData) else {"," return"," }"," let entry = EmojiEntry(emoji: emoji)"," completion(entry)"," }"," "," \/\/ 정의한 타임라인에 맞게 업데이트해서 보여질 내용"," func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) {"," guard let emoji = try? JSONDecoder().decode(Emoji.self, from: emojiData) else {"," return"," }"," let entry = EmojiEntry(emoji: emoji)"," let timeline = Timeline(entries: [entry], policy: .atEnd)"," completion(timeline)"," }","}"]},{"type":"paragraph","inlineContent":[{"type":"text","text":"TimelineProvider도 프로토콜이다."},{"type":"text","text":" "},{"type":"text","text":"Widget의 디스플레이를 업데이트 할 시기를 WidgetKit에 알려주는 타입이다."}]},{"type":"heading","level":3,"anchor":"TimelineReloadPolicy","text":"TimelineReloadPolicy"},{"type":"paragraph","inlineContent":[{"text":"가장 이른 날짜(the earliest date)를 나타내는 타입","type":"text"},{"text":" ","type":"text"},{"text":"타입 프로퍼티 3개를 가지고 있다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"atEnd"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"타임라인의 마지막 날짜가 지난 후 WidgetKit이 새 타임라인을 요청하도록 지정하는 policy이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"기본 refresh policy이다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"after(date:)"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"WidgetKit이 새 타임라인을 요청할 미래 날짜를 지정하는 policy"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"never"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"WidgetKit은 앱이 WidgetCenter를 사용하여 WidgetKit에 새 타임라인을 요청하도록 지시할 때 까지 다른 timeline을 요청하지 않는 policy이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉, 누가 지시할 때 까지 다른 timeline을 요청하지 않는다고 보면 될 것 같다."}]}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"text":"참고링크","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/www.agnosticdev.com\/blog-entry\/ios\/profiling-memory-allocations-ios-instruments"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/ios-development.tistory.com\/604"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/stackoverflow.com\/questions\/58121495\/memory-leak-when-displaying-a-modal-view-and-dismissing-it"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/velog.io\/@dlskawns96\/RxSwift-Closure%EC%97%90%EC%84%9C-Memory-Leak-%ED%94%BC%ED%95%98%EA%B8%B0","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/zeddios.tistory.com\/1088"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/zeddios.tistory.com\/1089"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/www.youtube.com\/watch?v=wOrkcdeui4U","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/widgetkit\/creating-a-widget-extension"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}]}],"sections":[],"schemaVersion":{"minor":3,"major":0,"patch":0},"metadata":{"role":"article","title":"220315 Memory Leak, RxSwift, withUnretained, Widget","roleHeading":"Article","modules":[{"name":"Ari's Notes"}]},"variants":[{"paths":["\/documentation\/arinote\/220315_memory_leak,_rxswift,_withunretained,_widget"],"traits":[{"interfaceLanguage":"swift"}]}],"kind":"article","references":{"https://i.imgur.com/r5iCn6o.png":{"identifier":"https:\/\/i.imgur.com\/r5iCn6o.png","alt":null,"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/r5iCn6o.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote/SwiftUI":{"role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/SwiftUI","url":"\/documentation\/arinote\/swiftui","title":"SwiftUI","kind":"article","abstract":[{"type":"text","text":"언능 UIKit만큼 SwiftUI를 더 잘 알고 싶다!"}],"type":"topic"},"https://i.imgur.com/DJHnOin.png":{"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/DJHnOin.png","traits":["1x","light"]}],"alt":null,"identifier":"https:\/\/i.imgur.com\/DJHnOin.png"},"https://i.imgur.com/BzmcpTf.png":{"identifier":"https:\/\/i.imgur.com\/BzmcpTf.png","alt":null,"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/BzmcpTf.png","traits":["1x","light"]}]},"https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW1":{"type":"link","url":"https:\/\/developer.apple.com\/library\/archive\/documentation\/General\/Conceptual\/ExtensibilityPG\/ExtensionScenarios.html#\/\/apple_ref\/doc\/uid\/TP40014214-CH21-SW1","identifier":"https:\/\/developer.apple.com\/library\/archive\/documentation\/General\/Conceptual\/ExtensibilityPG\/ExtensionScenarios.html#\/\/apple_ref\/doc\/uid\/TP40014214-CH21-SW1","title":"App Extension Programming Guide","titleInlineContent":[{"type":"text","text":"App Extension Programming Guide"}]},"https://i.imgur.com/thRuFvQ.png":{"identifier":"https:\/\/i.imgur.com\/thRuFvQ.png","alt":null,"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/thRuFvQ.png","traits":["1x","light"]}]},"https://i.imgur.com/qyXbFQd.png":{"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/qyXbFQd.png","traits":["1x","light"]}],"alt":null,"identifier":"https:\/\/i.imgur.com\/qyXbFQd.png"},"https://i.imgur.com/QAUgfyN.png":{"identifier":"https:\/\/i.imgur.com\/QAUgfyN.png","alt":null,"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/QAUgfyN.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/GbcKLb2.png":{"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/GbcKLb2.png","traits":["1x","light"]}],"alt":null,"identifier":"https:\/\/i.imgur.com\/GbcKLb2.png"},"https://i.imgur.com/YWf4Ejj.png":{"identifier":"https:\/\/i.imgur.com\/YWf4Ejj.png","alt":null,"type":"image","variants":[{"url":"https:\/\/i.imgur.com\/YWf4Ejj.png","traits":["1x","light"]}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220317_widget,_timeline,_decorator_pattern.json b/docs/data/documentation/arinote/220317_widget,_timeline,_decorator_pattern.json new file mode 100644 index 0000000..dfaeb3c --- /dev/null +++ b/docs/data/documentation/arinote/220317_widget,_timeline,_decorator_pattern.json @@ -0,0 +1 @@ +{"primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","type":"heading","text":"TIL (Today I Learned)","level":1},{"type":"paragraph","inlineContent":[{"type":"text","text":"3월 17일 (목)"}]},{"type":"heading","text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Widget 활동학습"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Decorator 패턴","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2,"type":"heading","text":"고민한 점 \/ 해결 방법"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[Widget 활동학습]","type":"text"}],"type":"strong"}]},{"type":"aside","content":[{"type":"paragraph","inlineContent":[{"text":"위젯 타겟을 추가하고, 생성된 파일의 Preview를 통해 구현된 위젯의 모습을 확인해 봅시다.","type":"text"}]}],"name":"Note","style":"note"},{"name":"Note","type":"aside","content":[{"type":"heading","text":"위젯을 구현하기 위한 Component 들에는 무엇이 있을까요?","level":3,"anchor":"%EC%9C%84%EC%A0%AF%EC%9D%84-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-Component-%EB%93%A4%EC%97%90%EB%8A%94-%EB%AC%B4%EC%97%87%EC%9D%B4-%EC%9E%88%EC%9D%84%EA%B9%8C%EC%9A%94"}],"style":"note"},{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/swiftui\/widget"}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/pNvORjO.png","type":"image"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"위젯 타입 (Configuration)"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"StaticConfiguration","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"IntentConfiguration","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"업데이트 타이밍 정보 (TimelineProvider)","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"TimelineEntry","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"위젯의 뷰를 구성하는 SwiftUI (EntryView)","type":"text"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"style":"note","name":"Note","type":"aside","content":[{"type":"heading","level":3,"anchor":"%EC%83%9D%EC%84%B1%EB%90%9C-Widget-%ED%8C%8C%EC%9D%BC%EC%97%90-%EC%9D%B4%EB%AF%B8-%EC%A0%95%EC%9D%98%EB%90%98%EC%96%B4%EC%9E%88%EB%8A%94-%EA%B0%81-%EA%B5%AC%EC%84%B1%EC%9A%94%EC%86%8C%EB%8A%94-%EC%9C%84%EC%A0%AF%EC%97%90%EC%84%9C-%EC%96%B4%EB%96%A4-%EC%97%AD%ED%95%A0%EC%9D%84-%ED%95%98%EA%B3%A0-%EC%9E%88%EC%9D%84%EA%B9%8C%EC%9A%94","text":"생성된 Widget 파일에 이미 정의되어있는 각 구성요소는 위젯에서 어떤 역할을 하고 있을까요?"}]},{"items":[{"content":[{"inlineContent":[{"code":"struct Provider: TimelineProvider","type":"codeVoice"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"위젯의 내용(스냅샷, 타임라인)을 업데이트하는 주기(Timeline)을 제공하는 객체"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"struct SimpleEntry: TimelineEntry","type":"codeVoice"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"위젯에 표시할 객체","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"struct MyWidgetEntryView : View"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"SwiftUI 에서 화면을 구성하는 View 구조체","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"@main struct MyWidget: Widget"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"위젯을 구성하는 메인 구조체"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"code":"struct MyWidget_Previews: PreviewProvider","type":"codeVoice"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"SwiftUI 프리뷰 구조체"}]}]}]}]}],"type":"unorderedList"},{"type":"aside","content":[{"type":"paragraph","inlineContent":[{"code":"EntryView를 조작해 어떤 변화가 있는지 살펴봅시다.","type":"codeVoice"},{"text":" ","type":"text"},{"type":"text","text":"Provider 의 각 메서드는 어떤 역할을 할까요?"}]}],"style":"note","name":"Note"},{"type":"aside","name":"Note","style":"note","content":[{"level":3,"type":"heading","text":"Placeholder, getSnapshot, getTimeline 메서드는 어떤 역할인가요?","anchor":"placeholder-getSnapshot-getTimeline-%EB%A9%94%EC%84%9C%EB%93%9C%EB%8A%94-%EC%96%B4%EB%96%A4-%EC%97%AD%ED%95%A0%EC%9D%B8%EA%B0%80%EC%9A%94"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"func placeholder(in context: Context) -> SimpleEntry"}]},{"items":[{"content":[{"inlineContent":[{"text":"WidgetKit 이 처음 위젯을 렌더링할 때 사용한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"code":"func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ())","type":"codeVoice"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"위젯 갤러리에서 위젯을 보여주기 위해, 스냅샷을 요청할 때 호출된다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"func getTimeline(in context: Context, completion: @escaping (Timeline) -> ())"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"위젯을 업데이트하는 타이밍(주기)와 policy 를 제공한다.","type":"text"}]}]}]}]}],"type":"unorderedList"},{"style":"note","content":[{"anchor":"Timeline%EC%9D%98-policy%EB%A5%BC-%EB%B3%80%EA%B2%BD%ED%95%98%EB%A9%B4-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%90%A0%EA%B9%8C%EC%9A%94","type":"heading","text":"Timeline의 policy를 변경하면 어떻게 될까요?","level":3}],"type":"aside","name":"Note"},{"items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"atEnd"},{"text":" : 주어진 Timeline 의 마지막 날짜(Date) 이후에 새로운 타임라인 요청하는 policy","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"after(date:)"},{"text":" : 마지막 날짜(Date) 이후 WidgetKit이 새 타임라인을 요청할 미래 날짜를 지정하는 policy","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Never"},{"type":"text","text":" : 누가 지시하기 전까지는 Timeline 을 요청하지 않는 policy"},{"type":"text","text":" "},{"text":"(WidgetKit은 앱이 WidgetCenter를 사용하여 WidgetKit에 새 타임라인을 요청하도록 지시 할 때 까지 다른 timeline을 요청하지 않는다)","type":"text"}]}]}],"type":"unorderedList"},{"style":"note","name":"Note","type":"aside","content":[{"type":"heading","text":"위젯은 언제 업데이트될까요?","anchor":"%EC%9C%84%EC%A0%AF%EC%9D%80-%EC%96%B8%EC%A0%9C-%EC%97%85%EB%8D%B0%EC%9D%B4%ED%8A%B8%EB%90%A0%EA%B9%8C%EC%9A%94","level":3}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"getTimeline 메서드가 TimelineEntry를 제공하고, TimelineEntry에는 위젯의 콘텐츠를 언제 업데이트 할지에 대한 Date가 들어있다!"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"WidgetKit은 그걸 보고 그 시간이 되면 그 때 나와야 할 view를 Widget에 전송한다.","type":"text"}]}]}]},{"name":"Note","type":"aside","style":"note","content":[{"text":"위젯이 구성되는 방식을 순차적으로 설명해봅시다.","type":"heading","level":3,"anchor":"%EC%9C%84%EC%A0%AF%EC%9D%B4-%EA%B5%AC%EC%84%B1%EB%90%98%EB%8A%94-%EB%B0%A9%EC%8B%9D%EC%9D%84-%EC%88%9C%EC%B0%A8%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%84%A4%EB%AA%85%ED%95%B4%EB%B4%85%EC%8B%9C%EB%8B%A4"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"TimelineEntry 가 보여줘야 하는 객체의 Model 및 날짜(Date 타입의 프로퍼티)를 갖고 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"TimelineProvider"}]}]},{"content":[{"inlineContent":[{"text":"EntryView","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Widget","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[Decorator Pattern]"}],"type":"strong"}]},{"level":3,"type":"heading","text":"Decorator 패턴이란?","anchor":"Decorator-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/1mNaHir.png","type":"image"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"기존 객체가 가진 동작들을 포함하는 특수 래퍼를 만들고, 새로운 기능을 추가할 수 있는 디자인 패턴"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"상속(Inheritance)과 합성(Composition)을 사용하여 객체에 동적으로 책임을 추가할 수 있게 하는 패턴","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"클래스의 코드를 전혀 바꾸지 않고도 객체에 새로운 임무를 부여하기 위해서 등장했다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"기존 객체를 감싸는 Wrapper를 만들고 해당 Wrapper 객체에 다른 기능들을 넣는 동작 때문에 Wrapper 패턴이라고도 한다.","type":"text"}]}]}],"type":"unorderedList"},{"anchor":"%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C","level":3,"type":"heading","text":"언제 사용할까?"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"다른 객체들에 영향을 주지 않고 개별 객체에 기능들을 추가하고 싶을 때 사용한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"추가한 기능들은 언제든 없앨 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"상속을 사용하여 기능을 확장하는 것이 힘들 경우에 사용한다.","type":"text"}]}]}]},{"level":3,"type":"heading","anchor":"%EA%B5%AC%EC%A1%B0","text":"구조"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/LhsZcVe.png"}]},{"inlineContent":[{"type":"codeVoice","code":"Component"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"동적으로 추가된 기능을 가질 수 있는 객체에 대한 인터페이스를 정의한다."}]}]}]},{"type":"paragraph","inlineContent":[{"code":"Concreate Component","type":"codeVoice"}]},{"items":[{"content":[{"inlineContent":[{"text":"Component 인터페이스의 기능을 실제로 구현한 객체","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"새로운 기능들이 추가될 수 있는 객체"}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"codeVoice","code":"Decorator"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Component 인터페이스를 따르는 객체를 참조할 수 있는 필드가 존재"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Component 인터페이스를 따르는 인터페이스를 정의"}]}]}]},{"inlineContent":[{"type":"codeVoice","code":"Concreate Decorator"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"구성 요소에 기능을 추가"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"code":"Client","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Component 인터페이스를 통해 모든 객체와 함께 동작할 수 있게 해준다."}],"type":"paragraph"}]}]},{"type":"heading","level":3,"text":"장점","anchor":"%EC%9E%A5%EC%A0%90"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"상속을 통한 하위 클래스를 만들지 않고도 객체의 기능을 확장할 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"런타임에서 객체에 책임을 추가하고 제거할 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"객체를 여러 데코레이터로 래핑하여 여러 동작을 합칠 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"객체 지향 프로그래밍에서 중요한 단일 책임 원칙(OCP)을 지킬 수 있다."}]}]}]},{"anchor":"%EB%8B%A8%EC%A0%90","type":"heading","level":3,"text":"단점"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"래퍼 스택에서 특정 래퍼를 제거하는 것이 어렵다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"데코레이터 기능이 데코레이터 스택 순서에 의존해야 한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"코드가 복잡해질 수 있다."}],"type":"paragraph"}]}]},{"text":"코드 예제","level":3,"type":"heading","anchor":"%EC%BD%94%EB%93%9C-%EC%98%88%EC%A0%9C"},{"type":"codeListing","syntax":"swift","code":["\/\/ Component","protocol Juice {"," func cost() -> Int","}","","\/\/ Concreate Component","class BananaJuice: Juice {"," func cost() -> Int {"," return 4000"," }","}","","class OrangeJuice: Juice {"," func cost() -> Int {"," return 3800"," }","}","","\/\/ Decorator","protocol JuiceDecorator: Juice {"," var wrappee: Juice { get set } \/\/ 객체를 참조할 wrappee를 정의","}","","\/\/ Concreate Decorator","class StrawberryBanana: JuiceDecorator {"," var wrappee: Juice"," "," init(wrappee: Juice) {"," self.wrappee = wrappee"," }"," "," func cost() -> Int {"," return wrappee.cost() + 1500"," }"," "," func addHoney() { \/\/ 새로운 기능 추가"," print(\"딸바주스에 꿀 추가 해드렸슴다.\")"," }","}","","\/\/ Concreate Decorator","class KiwiBanana: JuiceDecorator {"," var wrappee: Juice"," "," init(wrappee: Juice) {"," self.wrappee = wrappee"," }"," "," func cost() -> Int {"," return wrappee.cost() + 1000"," }"," "," func addIce() { \/\/ 새로운 기능 추가"," print(\"키위주스에 얼음추가 해드렸슴다. 시원하게드세여~\")"," }","}","","\/\/ Concreate Decorator","class Absolute: JuiceDecorator {"," var wrappee: Juice"," "," init(wrappee: Juice) {"," self.wrappee = wrappee"," }"," "," func cost() -> Int {"," return wrappee.cost() + 8000"," }"," "," \/\/ 새로운 기능 추가"," func moreJuice() {"," print(\"주스를 더 많이 넣어드렸어여~\")"," }"," "," func moreDrink() {"," print(\"술 더많이 타드렸어여~\")"," }","}","","let strawberryBananaJuice = StrawberryBanana(wrappee: BananaJuice())","print(strawberryBananaJuice.cost()) \/\/ 5500","strawberryBananaJuice.addHoney() \/\/ 딸바주스에 꿀 추가 해드렸슴다.","","let kiwiBananaJuice = KiwiBanana(wrappee: BananaJuice())","print(kiwiBananaJuice.cost()) \/\/ 5000","kiwiBananaJuice.addIce() \/\/ 키위주스에 얼음추가 해드렸슴다. 시원하게드세여~","","let screwDriver = Absolute(wrappee: OrangeJuice())","print(screwDriver.cost()) \/\/ 11800","screwDriver.moreJuice() \/\/ 주스를 더 많이 넣어드렸어여~",""]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"https:\/\/refactoring.guru\/design-patterns\/decorator","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/icksw.tistory.com\/244","type":"text"}]}]}]}]}]}]}],"schemaVersion":{"patch":0,"major":0,"minor":3},"variants":[{"paths":["\/documentation\/arinote\/220317_widget,_timeline,_decorator_pattern"],"traits":[{"interfaceLanguage":"swift"}]}],"sections":[],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/SwiftUI"]]},"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"220317 Widget, Timeline, Decorator Pattern"},"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220317_Widget,_Timeline,_Decorator_Pattern","interfaceLanguage":"swift"},"references":{"https://i.imgur.com/pNvORjO.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/pNvORjO.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/pNvORjO.png","type":"image"},"https://i.imgur.com/LhsZcVe.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/LhsZcVe.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/LhsZcVe.png","type":"image"},"https://i.imgur.com/1mNaHir.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/1mNaHir.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/1mNaHir.png","type":"image"},"doc://AriNote/documentation/AriNote/SwiftUI":{"role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/SwiftUI","url":"\/documentation\/arinote\/swiftui","title":"SwiftUI","kind":"article","abstract":[{"type":"text","text":"언능 UIKit만큼 SwiftUI를 더 잘 알고 싶다!"}],"type":"topic"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220318_uistoryboard,_spell_checking_in_xcode,_quick_help.json b/docs/data/documentation/arinote/220318_uistoryboard,_spell_checking_in_xcode,_quick_help.json new file mode 100644 index 0000000..bd38fe8 --- /dev/null +++ b/docs/data/documentation/arinote/220318_uistoryboard,_spell_checking_in_xcode,_quick_help.json @@ -0,0 +1 @@ +{"metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","title":"220318 UIStoryboard, Spell checking in Xcode, Quick help"},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220318_UIStoryboard,_Spell_checking_in_Xcode,_Quick_help","interfaceLanguage":"swift"},"primaryContentSections":[{"kind":"content","content":[{"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","type":"heading","level":1},{"type":"paragraph","inlineContent":[{"text":"3월 18일 (금)","type":"text"}]},{"level":2,"text":"학습 내용","type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"코드리뷰 코멘트 보면서 학습","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"오탈자 손쉽게 찾는 방법","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"퀵헬프와 문서화 주석?","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"UIStoryboard extension","type":"text"}]}]}],"type":"unorderedList"}]}]},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2,"type":"heading","text":"고민한 점 \/ 해결 방법"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[스토리보드로 설정한 ViewController 인스턴스를 간결하게 가져오기]"}]}],"type":"paragraph"},{"syntax":"swift","code":["func storyboardStart() {"," let storyboard = UIStoryboard(name: Constant.storyboardName, bundle: nil)"," guard let main = storyboard.instantiateViewController("," withIdentifier: Constant.storyboardID"," ) as? MainViewController else {"," return"," }"," main.viewModel = ProjectListViewModel(coordinator: self)"," navigationController.pushViewController(main, animated: false)","}"],"type":"codeListing"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"스토리보드를 가져오고, 그 스토리보드 내부에 있는 ViewController를 인스턴스화 해주려면 위와 같이 guard문으로 캐스팅을 해줘야한다."}],"type":"paragraph"}]}]},{"inlineContent":[{"type":"text","text":"이 작업을 매번 이렇게 해줄게 아니라, 좀 더 간단하게 해줄 수는 없을까? 하고 방법을 찾아보았다."}],"type":"paragraph"},{"type":"codeListing","code":["\/\/\/ case에 해당하는 스토리보드를 반환하는 열거형 타입","enum Storyboard: String {"," case main = \"Main\""," case detail = \"Detail\""," "," func storyboard() -> UIStoryboard {"," return UIStoryboard(name: rawValue, bundle: nil)"," }","}"],"syntax":"swift"},{"inlineContent":[{"text":"먼저 위와 같이 스토리보드라는 열거형 타입을 만들어서 각 케이스 별로 스토리보드 name을 rawValue로 가지고 있게 설계한 후, storyboard 메소드를 호출했을 때, 자신이 가지고있는 rawValue를 활용하여 UIStoryboard를 호출하도록 만들어주었다.","type":"text"}],"type":"paragraph"},{"type":"codeListing","syntax":"swift","code":["\/\/\/ Storyboard 타입의 프로퍼티와 associatedtype 타입의 ViewModel을 정의하고 있는 프로토콜","protocol StoryboardCreatable {"," associatedtype ViewModel"," static var storyboard: Storyboard { get }"," var viewModel: ViewModel { get set }","}","","extension StoryboardCreatable {"," static var storyboardIdentifier: String {"," return String(describing: Self.self)"," }"," \/\/\/ ViewController 내부에 ViewModel을 할당해주는 메소드"," mutating func configureWithViewModel(viewModel: ViewModel) {"," self.viewModel = viewModel"," }","}"]},{"inlineContent":[{"text":"그리고 ViewController에 채택해줄 StoryboardCreatable 프로토콜을 구현했다.","type":"text"},{"type":"text","text":" "},{"text":"extension으로 Identifier를 반환하는 프로퍼티와 ViewModel을 할당해주는 메소드를 구현해주었다.","type":"text"}],"type":"paragraph"},{"code":["extension StoryboardCreatable where Self: UIViewController {"," \/\/\/ 스토리보드에서 ViewController를 가져와서 ViewModel을 주입해주는 메소드"," \/\/\/ - Parameter viewModel: ViewController에 주입할 ViewModel"," \/\/\/ - Returns: ViewModel이 할당된 ViewController를 반환한다."," static func createFromStoryboard(viewModel: ViewModel) -> Self {"," var viewController: Self = UIStoryboard.createViewController()"," viewController.configureWithViewModel(viewModel: viewModel)"," return viewController"," }","}","","extension UIStoryboard {"," \/\/\/ 스토리보드에서 ViewController를 인스턴스화 시켜주는 메소드"," \/\/\/ - Returns: ViewController를 반환한다."," static func createViewController() -> T {"," let storyboard = UIStoryboard(name: T.storyboard.rawValue, bundle: nil)"," let createViewController = storyboard.instantiateViewController(withIdentifier: T.storyboardIdentifier)"," guard let viewController = createViewController as? T else {"," fatalError(\"Expected view controller with identifier \\(T.storyboardIdentifier)\")"," }"," return viewController"," }","}",""],"type":"codeListing","syntax":"swift"},{"inlineContent":[{"type":"text","text":"그리고 본격적으로 스토리보드를 통해 ViewController를 인스턴스화 해준 뒤 앞서 만든 configureWithViewModel 메소드를 활용하여 ViewModel을 할당해준 뒤 ViewController를 반환해주는 메소드를 구현해주었다."}],"type":"paragraph"},{"syntax":"swift","code":["class MainViewController: UIViewController, StoryboardCreatable {"," static var storyboard: Storyboard = .main"],"type":"codeListing"},{"inlineContent":[{"type":"text","text":"그리고 ViewController에서는 StoryboardCreatable를 채택해주고 storyboard 프로퍼티만 구현해주면 된다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"코디네이터 내부 메소드는 아래와 같이 단 3줄로 간결해졌다.","type":"text"}]},{"type":"codeListing","syntax":"swift","code":["\/\/ 리팩토링 전","func storyboardStart() {"," let storyboard = UIStoryboard(name: Constant.storyboardName, bundle: nil)"," guard let main = storyboard.instantiateViewController("," withIdentifier: Constant.storyboardID"," ) as? MainViewController else {"," return"," }"," main.viewModel = ProjectListViewModel(coordinator: self)"," navigationController.pushViewController(main, animated: false)","}","","\/\/ 리팩토링 후","func storyboardStart() {"," let viewModel = ProjectListViewModel(coordinator: self) \/\/ 뷰모델 생성"," let mainViewController = MainViewController.createFromStoryboard(viewModel: viewModel) \/\/ 뷰모델을 전달하여 ViewController 인스턴스화",""," navigationController.pushViewController(mainViewController, animated: false)","}"]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Xcode에서 오탈자를 손쉽게 찾는 방법]"}]}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"오탈자를 찾고싶은 파일에 접근하고 아래와 같은 경로로 들어가준다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Check를 하게되면 Xcode에서 빨간줄로 오탈자를 체크해준다.","type":"text"},{"text":" ","type":"text"},{"type":"image","identifier":"https:\/\/i.imgur.com\/I4GmuFW.png"}]}]}],"type":"unorderedList"},{"name":"Note","style":"note","type":"aside","content":[{"inlineContent":[{"text":"체크 예시","type":"text"}],"type":"paragraph"}]},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/23mGmUZ.png"}],"type":"paragraph"},{"anchor":"%EB%8B%A8%EC%B6%95%ED%82%A4","type":"heading","text":"단축키","level":3},{"inlineContent":[{"code":"Command","type":"codeVoice"},{"text":" + ","type":"text"},{"type":"codeVoice","code":";"}],"type":"paragraph"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[문서화 주석과 퀵헬프]"}]}],"type":"paragraph"},{"anchor":"%ED%80%B5%ED%97%AC%ED%94%84%EB%9E%80","type":"heading","level":3,"text":"퀵헬프란?"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Xcode에서 래퍼런스 문서의 요약된 내용을 보여주는 기능"}]},{"text":"문서화 주석","type":"heading","anchor":"%EB%AC%B8%EC%84%9C%ED%99%94-%EC%A3%BC%EC%84%9D","level":3},{"type":"paragraph","inlineContent":[{"type":"text","text":"변수, 상수, 클래스, 메소드, 함수, 열거형 등을 설명할 경우 마크업 문법에 따라 주석을 작성하면 다른 프로그래머가 ‘퀵헬프’를 통해 해당 내용을 확인할 수 있다."},{"text":" ","type":"text"},{"text":"형식을 맞추는 일은 번거롭지만 문서화에 큰 도움이 되므로 사용할 것을 권장한다.","type":"text"}]},{"level":3,"type":"heading","anchor":"%EB%AC%B8%EC%84%9C%ED%99%94-%EC%A3%BC%EC%84%9D-%EC%83%9D%EC%84%B1-%EB%8B%A8%EC%B6%95%ED%82%A4","text":"문서화 주석 생성 단축키"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"Command","type":"codeVoice"},{"text":" + ","type":"text"},{"type":"codeVoice","code":"Option"},{"type":"text","text":" + "},{"type":"codeVoice","code":"\/"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"문서화 주석에서 한줄 주석은 슬래시 세개, 여러줄 주석은 별표 두개를 사용한다."}]}]}],"type":"unorderedList"},{"syntax":"swift","code":["\/\/ MARK: - 문서화 주석","\/\/\/ 한 줄 문서화 주석","\/**"," 여러 줄 문서화 주석"," *\/"],"type":"codeListing"},{"type":"heading","level":3,"anchor":"%EC%BD%94%EB%93%9C%EC%98%88%EC%A0%9C","text":"코드예제"},{"code":[" \/**"," 자기 자신을 제외한 나머지 case들의 description을 문자열 배열 형태로 반환합니다."," "," 예시로 todo 케이스의 경우 doing과 done의 description이 담긴 문자열 배열이 반환됩니다."," ````"," let array = ProjectState.todo.excluded \/\/ [\"DOING\", \"DONE\"]"," ````"," *\/"," var excluded: [String] {"," switch self {"," case .todo:"," return [ProjectState.doing.description, ProjectState.done.description]"," case .doing:"," return [ProjectState.todo.description, ProjectState.done.description]"," case .done:"," return [ProjectState.todo.description, ProjectState.doing.description]"," }"," }"],"type":"codeListing","syntax":"swift"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/xUKjujA.png"}]},{"anchor":"%EB%A7%88%ED%81%AC%EC%97%85-%EB%AC%B8%EB%B2%95","text":"마크업 문법","type":"heading","level":3},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"‘-’, ‘+’, ‘*’ : 원형 글머리 기호 즉, 순서가 없는 리스트를 사용할 수 있다.","type":"text"},{"type":"text","text":" "},{"text":"• 1. 2. 3. … : 번호를 붙여서 순서 있는 리스트를 만들 수 있다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":"   ※ 번호는 크게 중요하지 않다. 자동으로 번호를 매겨 준다."},{"type":"text","text":" "},{"text":"• 줄바꿈 : 텍스트 간에 한 줄을 비워놓으면 된다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"• 문단 바꿈 : 바를 세 개 이상 사용하면 긴 줄로 문단을 나눠준다. "},{"text":" ","type":"text"},{"type":"text","text":"• 텍스트 기울임 : "},{"code":"*[텍스트]*","type":"codeVoice"},{"type":"text","text":" "},{"text":"• 텍스트 굵게 : ","type":"text"},{"code":"**[텍스트]**","type":"codeVoice"},{"text":" ","type":"text"},{"type":"text","text":"• 링크 : "},{"code":"[링크 내용](링크 주소)","type":"codeVoice"},{"text":" ","type":"text"},{"text":"• 큰 제목 : 큰 제목으로 사용할 텍스트 밑에 ‘==’ 등호 두 개 쓰기 or # [제목 작성]","type":"text"},{"type":"text","text":" "},{"type":"text","text":"• 중간 제목 : 중간 제목으로 사용할 텍스트 밑에 ‘-’ 바 쓰기 or ## [중간 제목 작성] "},{"type":"text","text":" "},{"type":"text","text":"• 코드 블록 : 네 칸 이상 들여쓰기 or 강세표(backquote, `)를 세 개 이상 한 쌍으로 묶기"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[스토리보드를 쓰지말아야하는 이유]","type":"text"}],"type":"strong"}]},{"text":"스토리보드의 장점","level":3,"anchor":"%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B3%B4%EB%93%9C%EC%9D%98-%EC%9E%A5%EC%A0%90","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"빠른 초기화","type":"codeVoice"}]},{"items":[{"content":[{"inlineContent":[{"text":"뷰를 만드는데 오래 걸리지 않는다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"시각화"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"앱의 흐름을 한눈에 볼 수 있다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"code":"낮은 진입장벽","type":"codeVoice"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"코드를 몰라도 초보자들이 이쁜 뷰를 만들 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"anchor":"%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B3%B4%EB%93%9C%EC%9D%98-%ED%95%9C%EA%B3%84%EC%99%80-%EB%8B%A8%EC%A0%90","type":"heading","level":3,"text":"스토리보드의 한계와 단점"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"생산성","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"앱이 점점 커지고 스토리보드 로딩시간이 길어지게 되면 오히려 생산성이 떨어지게 된다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"code":"가독성","type":"codeVoice"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"스토리보드가 방대하면 읽기도 어려워지고 난잡해보인다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"code":"협업 어려움","type":"codeVoice"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스토리보드 파일이 XML 포맷에다가 읽기도 어렵기 때문에 다수의 인원이 수정을 하게되면 Merge Conflict 처리가 큰 어려움으로 작용된다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"재사용성"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스토리보드로 만든 뷰는 재사용하기가 어렵다"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"번거로움"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"스토리보드로 만든 뷰들을 코드와 연결하기 위해서는 Identifier를 부여해줘야하는데 일일히 연결해주는게 생각보다 번거롭다.","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"text":"코드로 UI를 짜야하는 이유","level":3,"anchor":"%EC%BD%94%EB%93%9C%EB%A1%9C-UI%EB%A5%BC-%EC%A7%9C%EC%95%BC%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"간혹 스토리보드로는 구현 못하는 것들이 있다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"버튼에 Border Radius를 준다던가, 배경에 특정 패턴을 준다던가…"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"코드로 UI를 작성하다보면 API Reference Documentation을 자주 찾아보게 되고 자연스럽게 공식문서와 친해진다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"네비게이션 바, 탭바 컨트롤러 동작 원리 등을 자연스럽게 찾아보게 된다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"스토리보드에만 의존하다보면 언젠가는 한계에 직면하게 되고 극복하려면 코드를 써야한다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"anchor":"%EA%B7%B8%EB%A0%87%EB%8B%A4%EB%A9%B4-%EC%8A%A4%ED%86%A0%EB%A6%AC%EB%B3%B4%EB%93%9C%EB%8A%94-%EC%96%B8%EC%A0%9C%EC%8D%A8%EC%95%BC-%EC%A0%81%EC%A0%88%ED%95%A0%EA%B9%8C","type":"heading","text":"그렇다면 스토리보드는 언제써야 적절할까?","level":3},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"앱이 규모가 크지 않고 복잡하지 않아서 빠르게 만들 수 있을 때","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"스토리보드만으로 만들 수 있고 흐름을 한눈에 보고싶을 때","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"누군가와 협업하지 않고 혼자서 앱을 만들 때"}]}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/charmintern.tistory.com\/40"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/charmintern.tistory.com\/41","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/zeunny.tistory.com\/9"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"야곰의 스위프트 프로그래밍 3판 70p"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/sarunw.com\/posts\/spell-checking-in-xcode\/","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/gist.github.com\/markcleonard\/f1a19b27c00f8e1c82872705c1a06863","type":"text"}]}]}]}]}]}]}],"schemaVersion":{"major":0,"minor":3,"patch":0},"variants":[{"paths":["\/documentation\/arinote\/220318_uistoryboard,_spell_checking_in_xcode,_quick_help"],"traits":[{"interfaceLanguage":"swift"}]}],"sections":[],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"kind":"article","references":{"https://i.imgur.com/23mGmUZ.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/23mGmUZ.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/23mGmUZ.png","traits":["1x","light"]}]},"https://i.imgur.com/I4GmuFW.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/I4GmuFW.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/I4GmuFW.png","traits":["1x","light"]}]},"https://i.imgur.com/xUKjujA.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/xUKjujA.png","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/xUKjujA.png","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220321_app_distribution,_hashable.json b/docs/data/documentation/arinote/220321_app_distribution,_hashable.json new file mode 100644 index 0000000..7dde004 --- /dev/null +++ b/docs/data/documentation/arinote/220321_app_distribution,_hashable.json @@ -0,0 +1 @@ +{"sections":[],"primaryContentSections":[{"kind":"content","content":[{"type":"heading","anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","level":1},{"inlineContent":[{"text":"3월 21일 (월)","type":"text"}],"type":"paragraph"},{"text":"학습 내용","level":2,"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"App Distribution 활동학습"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Hashable"}],"type":"paragraph"}]}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"level":2,"type":"heading","text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[App Distribution]","type":"text"}],"type":"strong"}]},{"type":"paragraph","inlineContent":[{"type":"reference","isActive":true,"identifier":"https:\/\/help.apple.com\/xcode\/mac\/11.4\/"},{"type":"text","text":" 문서에서 아래와 같은 용어들의 내용을 알아보고 내 프로젝트에는 어떻게 적용되어야 하는지 확인해보자."}]},{"anchor":"Project","type":"heading","text":"Project","level":3},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"앱 개발에 필요한 파일과 리소스를 체계적으로 보관하는 공간.","type":"text"}]}]}],"type":"unorderedList"},{"type":"heading","level":3,"text":"Bundle ID \/ App ID","anchor":"Bundle-ID--App-ID"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱의 식별자"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"애플에서 앱을 구분하는 구분자이다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"heading","text":"Target","anchor":"Target","level":3},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"빌드하기 위한 제품 지정","type":"text"}],"type":"paragraph"}]}]},{"level":3,"type":"heading","anchor":"Scheme","text":"Scheme"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"‘이 환경에서 실행이 될거에요.’ 라는 환경설정 값 같은 것","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Target이랑 다른 점은 Target은 사용자에게 영향을 미치는 값이라면, Scheme은 테스트만을 위한 환경설정 값이라고 볼 수 있다."}]}]}],"type":"unorderedList"},{"type":"heading","level":3,"anchor":"Distribution-methods","text":"Distribution methods"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"App Store Connenct"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Provisioning Profile로 서명된 TestFlight 또는 App Store를 통해 앱을 배포하는 것.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉 앱스토어에 직접 배포하는 것을 뜻한다."}]}]}]}]},{"content":[{"inlineContent":[{"text":"Ad Hoc","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"내부 테스트용으로 베포하는 것을 말한다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Enterprise"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"사내 배포하는 것을 뜻함","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"라이센스가 따로있다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Copy App","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"macOS에서 배포하는 방식"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"주로 사이트 들어가서 다운받는 방식","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"Development","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테스터에게 배포하는 것을 뜻함"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Developer ID","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"macOS 앱의 경우 Apple에서 공증을 받았거나 방금 개발자 ID 인증서로 서명한 앱을 Mac App Store 외부에 배포하여 사용자에게 신뢰할 수 있는 개발자임을 보증하는 것을 뜻한다."}],"type":"paragraph"}]}]}]}]},{"type":"heading","level":3,"anchor":"Provisioning-Profile","text":"Provisioning Profile"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/IUTmwGx.png","type":"image"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"소프트웨어를 신뢰하는 건 Cretificate가 담당하고, Provisioning Profile는 각 디바이스가 개발자를 신뢰할 수 있는지 확인한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Provisioning Profile은 앱을 앱스토어 or 테스트 배포를 하기 위해 Cretificate, Devices, AppID를 하나로 묶는 역할을 한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱을 디바이스에 컴파일 하는데 사용한다."}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이 때 애플 개발자 페이지에 등록한 App Id와 실제 컴파일하려는 앱에 설정된 Bundle ID가 일치해야 설치된다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"누가(Certificate) \/ 어디서(Device) \/ 무엇을(App ID)"}]}]}]},{"anchor":"Signing-certificate","type":"heading","level":3,"text":"Signing certificate"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"애플이 개발자를 신뢰할 수 있는 보증서이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"애플의 하드웨어에서 특정 소프트웨어가 동작하는데, 이 때 애플의 허가가 필요하다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이 허가는 개발자가 Certificates를 생성하고 실행하여 xcode에 설치하면, 애플의 신뢰 대상이 되어 개발한 소프트웨어를 실행할 수 있다."}]}]}]},{"text":"Code signing","type":"heading","anchor":"Code-signing","level":3},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/0tvW2W1.png"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"앱에 서명한 사람을 식별하고 앱이 서명된 이후 수정되지 않았는지 확인할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"App Store에서 다운로드한 앱의 서명을 확인하여 서명이 잘못된 앱이 실행되지 않도록 한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"앱 번들의 실행 코드가 변경되면 서명이 무효화되기 때문에 앱의 실행코드는 서명으로 보호된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"유효한 서명을 통해 사용자는 앱이 Apple 소스에서 서명되었으며 서명된 이후 수정되지 않았음을 신뢰할 수 있다.","type":"text"}]}]}]},{"type":"heading","anchor":"Provisioning-Profile","level":3,"text":"Provisioning Profile"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/m3dY7og.png"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"빌드된 앱이 실행될 환경"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"앱을 지정된 장치에 설치하고 앱 서비스를 사용할 수 있도록 하는 provisioning profile이다."}],"type":"paragraph"}]}]},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/ncV7wd7.png","type":"image"}],"type":"paragraph"},{"type":"heading","text":"QnA","level":3,"anchor":"QnA"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/TZ4ZMNp.png"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"보통 개인 프로젝트 같은 경우 Provisioning profile, signing 등등… 모두 자동으로 되어있지만, 현업에 가면 자동으로 하지않는다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"왜 Test도 Target으로 분류되는걸까요?"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"테스트라는 것 자체도 하나의 앱으로 볼 수 있다."}]}]},{"content":[{"inlineContent":[{"text":"동작 주체이기 때문에 하나의 타깃으로 지정해줄 수 있다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[Hashable]"}],"type":"strong"}]},{"level":3,"anchor":"Hash%EB%9E%80","type":"heading","text":"Hash란?"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/qVkOfXe.png"}]},{"inlineContent":[{"type":"text","text":"데이터를 간단한 숫자로 변환한 것을 말한다."},{"type":"text","text":" "},{"type":"text","text":"원본 데이터를 특정 규칙에 따라 처리하여 간단한 숫자로 만든 것을 Hash Value라고 한다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"hashing","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"임의의 길이를 갖는 데이터(key)를 고정된 길이의 데이터(value)로 매핑하는 의미"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"hash function"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"key > value 매핑하는 함수","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"hash code"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"index 값"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"hash value"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"hash code에 해당되는 value 값"}]}]}],"type":"unorderedList"}]}]},{"level":3,"anchor":"Hashable","type":"heading","text":"Hashable"},{"inlineContent":[{"type":"text","text":"hashing 기능 프로토콜."},{"text":" ","type":"text"},{"text":"임의의 길이를 갖는 데이터(key)를 고정된 길이의 데이터(value로) 매핑한다.","type":"text"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"준수할 때 유의해야할 점","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"구조체의 경우 추가 구현없이 채택하는 것만으로도 사용 가능","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"클래스의 경우 직접 hash(into:)함수를 구현해주어야 함","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"열거형의 경우"}]},{"items":[{"content":[{"inlineContent":[{"text":"연관값이 없는 경우면 채택하지않아도 자동으로 구현된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"연관값이 있는 경우에는 연관값의 타입, 열거형 모두 채택하고 있어야 한다.","type":"text"}]}]}],"type":"unorderedList"}]}]},{"text":"코드예제","level":3,"anchor":"%EC%BD%94%EB%93%9C%EC%98%88%EC%A0%9C","type":"heading"},{"syntax":"swift","type":"codeListing","code":["struct Project {"," let id: UUID"," var title: String"," var description: String"," var date: Date"," var status: ProjectState"," ","}","extension Project: Hashable{"," func hash(into hasher: inout Hasher) {"," hasher.combine(self.id)"," }"," "," static func == (lhs: Self, rhs: Self) -> Bool {"," return lhs.hashValue == rhs.hashValue"," }","}"]},{"type":"paragraph","inlineContent":[{"type":"text","text":"나의 경우는 id가 UUID 타입이기도하고 고유하기 때문에, id로만 값을 비교했으면 했다."},{"type":"text","text":" "},{"type":"text","text":"따라서 위 코드의 경우는 id의 값을 해쉬하겠다고, combine 메소드 파라미터로 전달해주고 있다."},{"type":"text","text":" "},{"type":"text","text":"그리고 == 연산자를 재정의하여 hashValue를 비교하도록 구현해주었다."}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/ios-development.tistory.com\/675"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/babbab2.tistory.com\/149","type":"text"}]}]}],"type":"unorderedList"}]}]}]}],"metadata":{"title":"220321 App Distribution, Hashable","roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}]},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Xcode"]]},"schemaVersion":{"patch":0,"major":0,"minor":3},"kind":"article","identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220321_App_Distribution,_Hashable"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220321_app_distribution,_hashable"]}],"references":{"doc://AriNote/documentation/AriNote/Xcode":{"role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Xcode","url":"\/documentation\/arinote\/xcode","title":"Xcode","kind":"article","abstract":[{"text":"언젠가…. 좋아질거지 Xcode…?","type":"text"}],"type":"topic"},"https://i.imgur.com/qVkOfXe.png":{"type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/qVkOfXe.png"}],"identifier":"https:\/\/i.imgur.com\/qVkOfXe.png"},"https://i.imgur.com/TZ4ZMNp.png":{"type":"image","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/TZ4ZMNp.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/TZ4ZMNp.png"},"https://i.imgur.com/0tvW2W1.png":{"type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/0tvW2W1.png"}],"identifier":"https:\/\/i.imgur.com\/0tvW2W1.png"},"https://i.imgur.com/ncV7wd7.png":{"type":"image","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/ncV7wd7.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/ncV7wd7.png"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://help.apple.com/xcode/mac/11.4/":{"type":"link","url":"https:\/\/help.apple.com\/xcode\/mac\/11.4\/","identifier":"https:\/\/help.apple.com\/xcode\/mac\/11.4\/","title":"Xcode Help","titleInlineContent":[{"type":"text","text":"Xcode Help"}]},"https://i.imgur.com/IUTmwGx.png":{"type":"image","alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/IUTmwGx.png","traits":["1x","light"]}],"identifier":"https:\/\/i.imgur.com\/IUTmwGx.png"},"https://i.imgur.com/m3dY7og.png":{"type":"image","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/m3dY7og.png"}],"identifier":"https:\/\/i.imgur.com\/m3dY7og.png"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220322_flyweight,_facade.json b/docs/data/documentation/arinote/220322_flyweight,_facade.json new file mode 100644 index 0000000..0f1a36c --- /dev/null +++ b/docs/data/documentation/arinote/220322_flyweight,_facade.json @@ -0,0 +1 @@ +{"schemaVersion":{"minor":3,"patch":0,"major":0},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Architecture"]]},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220322_Flyweight,_Facade","interfaceLanguage":"swift"},"variants":[{"paths":["\/documentation\/arinote\/220322_flyweight,_facade"],"traits":[{"interfaceLanguage":"swift"}]}],"sections":[],"kind":"article","metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"title":"220322 Flyweight, Facade","roleHeading":"Article"},"primaryContentSections":[{"kind":"content","content":[{"level":1,"type":"heading","anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)"},{"inlineContent":[{"type":"text","text":"3월 22일 (화)"}],"type":"paragraph"},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading","level":2,"text":"학습 내용"},{"items":[{"content":[{"inlineContent":[{"text":"데코레이터 외에 나머지 패턴 공부","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"text":"고민한 점 \/ 해결 방법","level":2,"type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"text":"[Flyweight Pattern]","type":"text"}],"type":"strong"}]},{"level":3,"anchor":"%ED%94%8C%EB%9D%BC%EC%9D%B4%EC%9B%A8%EC%9D%B4%ED%8A%B8-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80","text":"플라이웨이트 패턴이란?","type":"heading"},{"inlineContent":[{"type":"text","text":"메모리 사용량과 처리를 최소화하기 위한 디자인 패턴."},{"type":"text","text":" "},{"text":"각 객체의 모든 데이터를 유지하는 대신 여러 객체 간에 State 공통 부분을 공유하여 메모리에 더 많은 객체를 넣을 수 있는 구조적 디자인 패턴이다.","type":"text"}],"type":"paragraph"},{"level":3,"text":"구조","type":"heading","anchor":"%EA%B5%AC%EC%A1%B0"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/soFAOqg.png","type":"image"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Flyweight","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"공유할 수 있는 정보를 갖는 플라이웨이트 객체를 정의."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"FlyweightFactory"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Flyweight 객체를 만들고 관리한다."}]}]},{"content":[{"inlineContent":[{"text":"Flyweight의 공유정보가 올바르게 공유되도록 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"클라이언트가 Flyweight 객체를 요청하면 팩토리가 이전에 만들어 놓은 동일한 Flyweight 객체가 있는지 찾아보고 없다면 새로 생성한다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Concreate Flyweight"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Flyweight 인터페이스를 구현하고 공유 상태에 대한 저장공간을 확보한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"여기에 저장하는 상태들은 intrinsic state(고유한 상태)라고 한다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Unshared Concreate Flyweight (Context)","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이 클래스는 Concrete Flyweight 객체를 자식으로 갖는다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"모든 Flyweight 서브 클래스를 공유할 필요는 없다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Client","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"Flyweight에 대한 참조를 유지한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Flyweight 객체의 각각의 상태를 처리하거나 저장한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"level":3,"anchor":"%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C","type":"heading","text":"언제 사용할까?"},{"items":[{"content":[{"inlineContent":[{"text":"싱글톤을 사용하지만, 구성이 다른 여러개의 비슷한 공유 인스턴스가 필요할 때 사용한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"heading","text":"장점","anchor":"%EC%9E%A5%EC%A0%90","level":3},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"앱에 유사한 인스턴스를 대량으로 만들어야할 때 메모리 절약 가능"}]}]}]},{"text":"단점","type":"heading","anchor":"%EB%8B%A8%EC%A0%90","level":3},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Flyweight Factory에서 인스턴스가 이미 존재하는지 확인하기 위한 데이터 검색을 수행하는 런타임 비용이 추가 발생"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"메모리 절약과 trade-off라서 어쩔 수 없긴 하지만 공유하는 인스턴스가 많아질 수록 런타임 비용이 커진다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"text":"코드가 복잡해질 수 있다.","type":"text"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Facade Pattern]"}]}],"type":"paragraph"},{"anchor":"%ED%8C%8C%EC%82%AC%EB%93%9C-%ED%8C%A8%ED%84%B4%EC%9D%B4%EB%9E%80","level":3,"text":"파사드 패턴이란?","type":"heading"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/lDHLA6g.png"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"라이브러리, 프레임워크, 혹은 복잡한 클래스들의 집합에 대한 단순화된 인터페이스를 제공하는 디자인패턴이다."},{"type":"text","text":" "},{"text":"하나의 시스템을 서브 시스템들의 조합으로 구성하면 복잡성을 줄이는데 도움이 된다.","type":"text"},{"text":" ","type":"text"},{"text":"이러한 설계의 목표는 서브 시스템 간 통신 및 종속성을 최소화하는 것인데, 이를 위한 방법으로 서브 시스템의 기능을 단순한 인터페이스를 제공하는 파사드 객체를 사용하는 것이다.","type":"text"}],"type":"paragraph"},{"text":"구조","type":"heading","level":3,"anchor":"%EA%B5%AC%EC%A1%B0"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/TRWfVRB.png"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Facade"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"어떤 Subsystem 클래스가 클라이언트의 요청에 응답해야하는지 알고 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"클라이언트의 요청을 적절한 Subsystem에게 전달한다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Subsystem"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Subsystem 기능을 구현한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Facade 객체에서 전달받은 요청을 처리한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"서브 시스템 클래스들은 Facade 객체의 존재를 모른다.","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Client"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"클라이언트는 서브 시스템 객체를 직접 호출하는 대신 Facade를 사용한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"type":"heading","level":3,"text":"언제 사용할까?","anchor":"%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"서브 시스템을 계층화하고 싶을 때","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"복잡한 서브 시스템들을 간단하게 사용하기 위한 인터페이스를 만들고 싶을 때","type":"text"}]}]}]},{"text":"장점","type":"heading","anchor":"%EC%9E%A5%EC%A0%90","level":3},{"items":[{"content":[{"inlineContent":[{"text":"서브 시스템의 복잡성으로부터 코드를 분리할 수 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"서브 시스템으로 부터 클라이언트를 보호하고 클라이언트가 서브 시스템을 사용하기 쉽게 만들어준다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Facade를 사용하여 시스템과 객체 간 종속성을 계층화할 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"컴파일 종속성을 줄여 서브 시스템이 변경될 때 컴파일 시간을 줄여준다."}]}]}],"type":"unorderedList"},{"level":3,"text":"단점","anchor":"%EB%8B%A8%EC%A0%90","type":"heading"},{"items":[{"content":[{"inlineContent":[{"text":"Facade 객체는 앱의 모든 클래스에 결합된 객체가 될 수도 있다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"inlineContent":[{"type":"text","text":"https:\/\/icksw.tistory.com\/246"},{"text":" ","type":"text"},{"text":"https:\/\/refactoring.guru\/design-patterns\/facade","type":"text"}],"type":"paragraph"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"https:\/\/icksw.tistory.com\/246","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/icksw.tistory.com\/247","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/refactoring.guru\/design-patterns\/facade"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/it-mesung.tistory.com\/183"}]}]}]}]}],"type":"unorderedList"}]}],"references":{"doc://AriNote/documentation/AriNote/Architecture":{"role":"collectionGroup","kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Architecture","abstract":[{"text":"공부 많이해서 상황에 따라 적절한 아키텍처를 선택해보자.","type":"text"}],"title":"Architecture","type":"topic","url":"\/documentation\/arinote\/architecture"},"https://i.imgur.com/TRWfVRB.png":{"identifier":"https:\/\/i.imgur.com\/TRWfVRB.png","variants":[{"url":"https:\/\/i.imgur.com\/TRWfVRB.png","traits":["1x","light"]}],"alt":null,"type":"image"},"https://i.imgur.com/lDHLA6g.png":{"alt":null,"variants":[{"url":"https:\/\/i.imgur.com\/lDHLA6g.png","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/i.imgur.com\/lDHLA6g.png"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/soFAOqg.png":{"identifier":"https:\/\/i.imgur.com\/soFAOqg.png","variants":[{"url":"https:\/\/i.imgur.com\/soFAOqg.png","traits":["1x","light"]}],"alt":null,"type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220324_-----_----.json b/docs/data/documentation/arinote/220324_-----_----.json new file mode 100644 index 0000000..edaf5e5 --- /dev/null +++ b/docs/data/documentation/arinote/220324_-----_----.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Architecture"]]},"metadata":{"roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"title":"220324 디자인패턴 활동학습","role":"article"},"sections":[],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220324_-----_----","interfaceLanguage":"swift"},"primaryContentSections":[{"content":[{"level":1,"type":"heading","anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)"},{"type":"paragraph","inlineContent":[{"type":"text","text":"3월 24일 (목)"}]},{"text":"학습 내용","type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"디자인패턴 활동학습"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Chain of Responsibility"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Command"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Interpreter"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Visitor"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Proxy"}]}]},{"content":[{"inlineContent":[{"text":"Template","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Bulider"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Factory"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Abstract Factory","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","type":"heading","text":"고민한 점 \/ 해결 방법","level":2},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Chain of Responsibility Pattern]"}]}]},{"inlineContent":[{"text":"요청을 처리하는 모듈을 만들어서 체인 형태로 구성하는 디자인 패턴.","type":"text"},{"text":" ","type":"text"},{"text":"하나의 모듈이 어떤 작업을 처리한 다음 다른 모듈로 결과를 전달하고, 다른 모듈도 해야 할 작업을 처리한 다음 또 다른 모듈로 전달한다.","type":"text"}],"type":"paragraph"},{"anchor":"%EA%B5%AC%EC%A1%B0","text":"구조","type":"heading","level":3},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Handler"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"클라이언트의 요청을 처리하기 위한 인터페이스를 정의"}]}]},{"content":[{"inlineContent":[{"text":"현재 핸들러에서 처리하지 않을 것이라면 요청을 넘길 다음 핸들러를 구현한다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"처리할 수 없는 요청인 경우 마지막 핸들러까지 가서도 처리되지 않을 수 있다. 마지막 핸들러는 다음 핸들러를 갖지 않을 것이므로 이는 선택적으로 만들면 된다."}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Concrete Handler","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"요청을 처리한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"다음 핸들러에 작업을 넘긴다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Client","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Chain의 첫번째 Concrete Handler 객체에 요청을 한다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"이를 요청하기 위해 클라이언트가 첫번째 Concrete Handler에 접근할 수 있어야 한다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"클라이언트의 요청은 Chain에 존재하는 모든 핸들러로 전송될 수 있다.","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"level":3,"text":"언제 사용할까?","anchor":"%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C","type":"heading"},{"items":[{"content":[{"inlineContent":[{"text":"유사한 이벤트를 처리하지만 이벤트의 타입, 속성, 기타 항목에 따라 달라지는 객체 그룹이 있을 때 책임 연쇄 패턴을 사용하면 좋다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"heading","text":"장점","anchor":"%EC%9E%A5%EC%A0%90","level":3},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"요청 서리 순서를 제어할 수 있다"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"결합도를 감소시켜준다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"체인에 존재하는 객체들은 요청을 처리할 것인지 넘길 것인지만 판단하면 되고, 체인의 다른 객체들이 뭘하는지 알 필요가 없다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Single Responsibility Principle(단일 책임 원칙)을 지킬 수 있다. 작업을 수행하는 클래스, 작업을 호출하는 클래스를 분리할 수 있다."}]}]},{"content":[{"inlineContent":[{"text":"Open, Closed Principle(개방\/폐쇄 원칙)을 지킬 수 있다. 기존 클라이언트의 코드를 바꾸지 안혹 새로운 핸들러를 앱에 추가할 수 있다.","type":"text"}],"type":"paragraph"}]}]},{"text":"단점","type":"heading","anchor":"%EB%8B%A8%EC%A0%90","level":3},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"처리되지 않는 요청이 있을 수 있지만, 요청할 때는 이걸 알 수가 없다. 체인이 끝까지 가야 알 수 있기 때문이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"체인을 잘못만들 경우 사이클이 발생할 수 있다."}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Command Pattern]"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"요청이나 간단한 작업을 객체로 캡슐화하여 이를 큐나 log로 처리하는 방법이다."},{"type":"text","text":" "},{"type":"text","text":"이렇게 캡슐화한 작업은 실행 취소를 할 수도 있게 만들 수 있다."}]},{"type":"heading","text":"구조","anchor":"%EA%B5%AC%EC%A1%B0","level":3},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Command"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"작업을 실행하기 위한 인터페이스를 정의한다"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Command를 실행하기 위한 하나의 메서드로 이루어진 경우가 많다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Concrete Command","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Receiver 객체와 작업 사이 바인딩을 정의한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Receiver에서 해당 작업을 호출하여 실행한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"직접 작업을 수행하는 것이 아닌 Receiver에게 전달하기 위한 목적을 갖고 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"코드를 단순화 하기 위해 클래스를 합칠 수도 있다."}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Client"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Concrete Command 객체를 만들고 Receiver를 설정한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Receiver의 인스턴스를 포함한 작업에 필요한 모든 매개변수를 Command의 생성자에 전달하여 작업을 처리한다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Invoker"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Invoker 클래스에는 명령 객체에 대한 참조를 저장하기 위한 필드가 있어야 한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"클라이언트가 생성자를 통해 Invoker 객체를 만들 때 Command 객체를 받게 된다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"요청을 Receiver에게 직접 보내는 것이 아닌 해당 요청의 시작을 담당으로 한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Receiver"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"웬만한 클래스는 Receiver 역할을 할 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"요청된 작업을 수행한다."}]}]}]}]}],"type":"unorderedList"},{"text":"언제 사용할까?","type":"heading","anchor":"%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C","level":3},{"type":"paragraph","inlineContent":[{"text":"작업르 캡슐화해서 Receiver에게 보내서 처리하고 싶을 때 사용하면 좋다.","type":"text"}]},{"level":3,"text":"장점","type":"heading","anchor":"%EC%9E%A5%EC%A0%90"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"단일 책임 원칙을 준수한다. Command 객체를 통해 작업을 수행하는 객체와 작업을 호출하는 객체를 나눌 수 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"개방 폐쇄 원칙을 준수한다. 클라이언트의 코드를 수정하지 않고도 새로운 Command를 추가할 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"실행 취소, 다시 실행을 구현할 수 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"작업의 시작을 지연시킬 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"여러 개의 단순한 명령을 조합해서 복잡한 명령을 만들 수 있다.","type":"text"}]}]}],"type":"unorderedList"},{"anchor":"%EB%8B%A8%EC%A0%90","type":"heading","text":"단점","level":3},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Receiver, Invoker 사이에 관계를 도입해야 하므로 코드가 복잡해질 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Interpreter Pattern]"}]}]},{"type":"paragraph","inlineContent":[{"text":"언어가 주어지면 해당 표현을 사용하여 언어로 문장을 해석하는 인터프리터를 사용하여 문법 표현을 정의하는 방법이다.","type":"text"}]},{"anchor":"%EA%B5%AC%EC%A1%B0","level":3,"type":"heading","text":"구조"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Abstract Expression"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Abstract Syntax Tree의 모든 노드에서 사용할 Interpret 작업을 정의한다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Terminal Expression","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"터미널 기호와 관련된 작업을 구현한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"문장의 모든 터미널 기호에 인스턴스가 필요하다."}]}]},{"content":[{"inlineContent":[{"text":"여기서 말하는 터미널 기호라고 함은 어떤 문장에서 의미있는 최소의 단위를 뜻한다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Nonterminal Expression"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"하나의 클래스는 문법이 가진 모든 규칙을 필요로 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"R1… Rn Symbol 각각에 대한 AbstractExpression 타입의 인스턴스 변수를 유지한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Grammar에서 Nonterminal Symbol에 대한 해석 연산을 구현한다. Interpret는 R1… Rn까지의 규칙을 나타내는 변수를 통해 재귀적 방식으로 해석한다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Context","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Interpreter가 해석할 문장"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해석의 대상이 되는 문자열을 Expression 할 수 있도록 전달한다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Client"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"실제로 Interpreter를 호출하는 부분","type":"text"}]}]}],"type":"unorderedList"}]}]},{"level":3,"anchor":"%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C","text":"언제 사용할까?","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"어떤 언어의 문법을 정의하고 해석할 때 주로 활용한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정규표현식, 번역기 등이 좋은 예시가 될 듯"}]}]}],"type":"unorderedList"},{"text":"장점","level":3,"type":"heading","anchor":"%EC%9E%A5%EC%A0%90"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"새로운 패턴에 대응하는 Expression을 쉽게 추가할 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해석 대상이 규칙만 가지고 있다면 적용이 가능하므로 활용범위가 무궁무진하다."}]}]}],"type":"unorderedList"},{"anchor":"%EB%8B%A8%EC%A0%90","text":"단점","type":"heading","level":3},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"하나의 패턴이나 규칙에 1개 이상의 Expression 클래스가 생성되므로 복잡한 문법을 갖고 있는 언어에 대해서는 관리가 쉽지 않다.","type":"text"}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"type":"text","text":"[Visitor Pattern]"}],"type":"strong"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"알고리즘을 작동하는 객체에서 분리할 수 있는 디자인 패턴이다."}]},{"anchor":"%EA%B5%AC%EC%A1%B0","level":3,"type":"heading","text":"구조"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/fHrVIWZ.png"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Visitor"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Concrete Element를 매개변수로 사용할 수 있는 Visit 메서드들을 정의한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"언어가 오버로딩을 지원하는 경우 메서드 이름이 같을 순 있지만 매개변수 타입은 달라야 한다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Concrete Visitor"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"서로 다른 Concrete Element에 맞게 메서드를 구현한다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Element","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Visitor를 Accept하는 메서드를 정의한다. Accept 메서드는 Visitor 객체를 매개변수로 사용해야한다.","type":"text"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Concrete Element","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Accept 메서드를 구현한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"Accept 메서드는 Element 객체에서 사용할 적절한 Visitor의 메서드를 호출하는 역할을 한다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Client","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"text":"컬레션이나 트리와 같은 자료구조를 나타낸다","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"level":3,"text":"언제 사용할까?","type":"heading","anchor":"%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"자료구조(Element)와 자료구조를 처리하는 로직(Visitor)를 분리하여, 로직이 자주 변경될 경우에 사용한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"객체들이 복잡한 구조의 모든 Element에 대해 작업을 수행해야하는 경우.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"클래스 계층 구조에서 몇몇 단계에 있는 클래스에만 의미있는 메서드가 존재할 때."}],"type":"paragraph"}]}],"type":"unorderedList"},{"anchor":"%EC%9E%A5%EC%A0%90","level":3,"type":"heading","text":"장점"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"작업 대상(방문 공간) 과 작업 항목(방문 공간을 가지고 하는 일)을 분리시킨다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"작업 대상(방문 공간) 은 단지 데이터를 담고있는 자료구조로 만들고, 작업 주체(방문자) 는 visit() 안에 이 작업 대상을 입력받아 작업 항목을 처리하면 된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"즉, 데이터와 알고리즘이 분리되어, 데이터의 독립성을 높여준다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"작업 대상의 입장에서는 accept() 로 인터페이스를 통일시켜, 사용자에게 동일한 인터페이스를 제공한다."}]}]}],"type":"unorderedList"},{"type":"heading","level":3,"text":"단점","anchor":"%EB%8B%A8%EC%A0%90"},{"items":[{"content":[{"inlineContent":[{"text":"새로운 작업 대상(방문 공간)이 추가될 때마다 작업 주체(방문자)도 이에 대한 로직을 추가해야 한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"두 객체 (방문자와 방문 공간)의 결합도가 높아진다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"서로 visit() 과 accept() 에 의존하므로.","type":"text"}]}]}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Proxy Pattern]"}]}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"다른 객체에 대한 접근을 제어할 수 있도록 surrogate, placeholder를 제공할 수 있는 구조 패턴","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"비즈니스 로직을 수행하는 실제 객체가 들어갈 자리에 대리자(Proxy)를 대신 투입하여 흐름제어 수행"}]},{"anchor":"%EA%B5%AC%EC%A1%B0","type":"heading","level":3,"text":"구조"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/40W9dJ6.png"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Subject"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"서비스의 인터페이스를 정의한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"프록시는 서비스 객체로 사용할 수 있도록 인터페이스를 준수해야 한다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"RealSubject"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"서비스는 비즈니스 로직을 제공하는 클래스다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Proxy"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"프록시 객체에는 서비스 객체를 참조하는 필드가 있다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Lazy initialization, logging, access control, caching 등과 같은 작업을 완료한 뒤 클라이언트의 요청을 서비스 객체에 전달한다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"참조하는 서비스 객체와 동일한 인터페이스를 준수한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"프록시에는 Remote Proxy, Virtual Proxy, Protection Proxy가 있으며 각각의 책임으로 구분한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Remote Proxy","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"요청을 처리하고 서비스 객체에 이를 전달하는 역할을 한다.","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Virtual Proxy"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"서비스 객체에 대한 정보를 캐싱하여 접근을 연기한다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Protection Proxy"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"특정 작업을 요청한 객체가 해당 작업을 수행할 권한을 가지고 있는지 확인"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]}]},{"content":[{"inlineContent":[{"text":"Client","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"서비스 객체가 필요한 모든 코드를 프록시에 전달하기 위해 동일한 인터페이스를 사용하는 서비스와 프록시 객체와 함께 작동해야한다."}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"text":"언제 사용할까?","level":3,"anchor":"%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C","type":"heading"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"원본 객체가 실제로 사용될 때까지 생성 및 초기화를 위한 처리를 미루기 위해 원본 객체 대신 프록시를 노출하여 원본 객체에 대한 접근을 제어"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"큰 이미지나 그래픽 같은 큰 데이터가 로딩되는데 시간이 걸리기 때문에 처음부터 데이터를 로딩하지 않고 필요한 순간에 로딩하기 위해 사용","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"원본 객체의 코드를 바꾸지 않고 추가적인 기능을 넣고 싶을 때","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"바로 생성하는 것이 아닌 생성을 지연시키고 싶을 때"}]}]}],"type":"unorderedList"},{"type":"heading","text":"장점","level":3,"anchor":"%EC%9E%A5%EC%A0%90"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"클라이언트가 알지 못하는 상태에서 서비스 객체를 제어할 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"서비스 객체의 생명 주기를 관리할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"프록시는 서비스 객체가 준비되지 않았거나 사용할 수 없는 경우에도 작동한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"서비스나 클라이언트를 변경하지 않고 새로운 프록시를 도입할 수 있다.","type":"text"}]}]}],"type":"unorderedList"},{"anchor":"%EB%8B%A8%EC%A0%90","type":"heading","text":"단점","level":3},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"새로운 클래스를 도입해야 하므로 코드가 복잡해진다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"응답이 지연될 수 있다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[Template Method Pattern]"}]}]},{"inlineContent":[{"type":"text","text":"알고리즘 단계를 메서드로 정의하고 하위클래스에서 알고리즘 단계를 재정의하는 패턴"}],"type":"paragraph"},{"text":"구조","level":3,"anchor":"%EA%B5%AC%EC%A1%B0","type":"heading"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/WwLGCRY.png","type":"image"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Abstract Class(Application)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"알고리즘을 단계적으로 작동하는 메서드들과 이들을 실제로 호출하는 Template Method를 정의한다."}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Concrete Class(My Application)"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Abstract Class에서 정의한 단계적으로 작동하는 메서드들은 override할 수 있지만 Template Method는 override할 수 없다.","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"text":"언제 사용할까?","level":3,"type":"heading","anchor":"%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"클라이언트가 특정 단계를 변경, 제어하면서도 알고리즘의 실행 순서를 보장하고자 할 때"}],"type":"paragraph"}]}]},{"type":"heading","level":3,"text":"장점","anchor":"%EC%9E%A5%EC%A0%90"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"중복된 코드를 Extension으로 정의하기 때문에 중복 코드를 줄일 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"하위 객체를 쉽게 추가, 확장할 수 있다. 알고리즘의 다른 단계에 비교적 적은 영향을 미치기 때문.","type":"text"}]}]}],"type":"unorderedList"},{"type":"heading","level":3,"text":"단점","anchor":"%EB%8B%A8%EC%A0%90"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이미 정의된 알고리즘만 사용할 수 있기 때문에, 일부 클라이언트의 구현이 제한되는 경우가 생길 수 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Extension의 정의부를 변경할 때, 하위 클래스 또한 수정이 필요해질 수 있다.","type":"text"}]}]}]},{"type":"thematicBreak"},{"inlineContent":[{"inlineContent":[{"text":"[Factory Pattern]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"객체를 생성하는 디자인 패턴인 Crational Pattern의 일종이다."},{"text":" ","type":"text"},{"text":"인스턴스의 생성을 책임지는 Factory 타입을 정의하여 Factory 타입의 메서드를 통하여 인스턴스를 생성하는 것을 말한다.","type":"text"}],"type":"paragraph"},{"anchor":"%EA%B5%AC%EC%A1%B0","level":3,"text":"구조","type":"heading"},{"inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/sClOU1c.png"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"객체를 생성하는 Factory"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이로부터 생성된 객체 Product","type":"text"}]}]}],"type":"unorderedList"},{"text":"언제 사용할까?","level":3,"anchor":"%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"비슷하지만 세부사항만 다른 여러 인스턴스들을 생성할 필요가 있을 때","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"인스턴스의 생성 로직이 너무 복잡하여 분리가 필요할 때"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"인스턴스를 생성하기 위해서 서로 다른 여러 타입을 의존해야할 때"}],"type":"paragraph"}]}]},{"anchor":"%EC%9E%A5%EC%A0%90","text":"장점","level":3,"type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"생성할 타입에 대한 불필요한, 복잡한 정보를 숨기고 꼭 필요한 조건들만 가지고 인스턴스 생성을 가능하게 해준다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"복잡한 객체 생성 로직들을 하나의 Factory 타입에 모아 관리할 수 있다."}]}]}]},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"text":"[Abstract Factory Pattern]","type":"text"}]}]},{"inlineContent":[{"text":"구체적인 클래스에 의존하지 않고 관련 객체들을 모으기 위한 인터페이스를 제공하는 것","type":"text"},{"type":"text","text":" "},{"type":"text","text":"공통된 요소들을 갖는 개별 팩토리 그룹을 캡슐화 하는 방법을 제공한다."},{"type":"text","text":" "},{"text":"관련있는 여러 객체를 일관된 방식으로 생성하는 경우 유용하다.","type":"text"}],"type":"paragraph"},{"type":"heading","anchor":"%EA%B5%AC%EC%A1%B0","level":3,"text":"구조"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/ase7E1i.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Abstract Factory","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"인터페이스는 각각의 추상 제품을 생성하기 위한 메소드들을 선언한다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Concrete Factory","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"Abstract Factory의 생성 방법을 구현한다. 각 Concrete Factory는 product의 특정 변형에 해당하며 해당 product 변형만 생성한다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"Client","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"abstract interfaces를 통해 개체와 통신하는 한 모든 concrete factory\/product 변형으로 작업할 수 있습니다."}],"type":"paragraph"}]}]}]}]},{"anchor":"%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C","level":3,"text":"언제 사용할까?","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"앱이 객체의 생성, 구성, 표현방법과 독립적이어야 할 때","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"앱이 여러 객체 중 하나로 구성되어야 할 때","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"관련 객체들이 함께 사용할 수 있게 설계할 때"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"앱의 클래스 라이브러리를 제공하고 구현이 아닌 인터페이스만 표시할 때"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"heading","text":"특징","level":3,"anchor":"%ED%8A%B9%EC%A7%95"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"코드를 변경하지 않고 런타임 시점에 환경(조건)에 따른 적절한 객체를 사용할 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"각 객체 간의 결합도를 낮출 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"다만 너무 깊은 단계로 구현하는 경우 복잡해지거나 코드의 흐름을 찾기가 어렵다.","type":"text"}]}]}],"type":"unorderedList"},{"type":"thematicBreak"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[Builder Pattern]"}],"type":"strong"}]},{"type":"paragraph","inlineContent":[{"text":"이니셜라이저에 여러개의 파라미터를 넣는 대신, 차례차례 값을 넣어서 객체를 생성하는 것을 도와주는 패턴이다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"setter 메서드와 체이닝을 통해서 특정 객체를 조합해가는 패턴이다."}]},{"type":"heading","level":3,"anchor":"%EA%B5%AC%EC%A1%B0","text":"구조"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/68mu4Em.png","type":"image"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Product"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Builder를 이용해서 Director가 만들어낸 최종 객체 \/ 아래 예시에서는 UILabel","type":"text"}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"Builder"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"객체를 생성하는 추상 인터페이스"}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"ConCreateBuilder","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"Builder의 구현클래스 \/ 예시에서는 UILabelBuilder"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Director","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"객체를 생성하는 책임을 가짐 \/ Director를 통해서 생성이 이루어져야 한다"}]}]}]}]}],"type":"unorderedList"},{"type":"heading","level":3,"anchor":"%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C","text":"언제 사용할까?"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"복잡한 객체를 생성할 때"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"여러 속성을 가진 복잡한 객체를 간결하게 생성하고 싶을 때"}]}]}],"type":"unorderedList"},{"text":"장점","anchor":"%EC%9E%A5%EC%A0%90","level":3,"type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"객체가 만들어지는 과정을 세밀하게 제어할 수 있다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"한번에 모든 프로퍼티를 만드는 방법과 다르게, 빌더 패턴은 단계별로 객체를 만들기 때문에 이를 세밀하게 제어할 수 있다는 의미"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"코드의 길이는 짧아지고 의미는 더 명확해진다."}],"type":"paragraph"}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/wiry-launch-6ab.notion.site\/Builder-Pattern-9fdefa53daff4a81b53ab9e0db9fc250"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/refactoring.guru\/design-patterns\/abstract-factory","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/refactoring.guru\/design-patterns\/template-method"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/icksw.tistory.com\/235"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/icksw.tistory.com\/236","type":"text"}],"type":"paragraph"}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"variants":[{"paths":["\/documentation\/arinote\/220324_-----_----"],"traits":[{"interfaceLanguage":"swift"}]}],"schemaVersion":{"minor":3,"major":0,"patch":0},"kind":"article","references":{"https://i.imgur.com/ase7E1i.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/ase7E1i.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/ase7E1i.png"}]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/sClOU1c.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/sClOU1c.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/sClOU1c.png"}]},"doc://AriNote/documentation/AriNote/Architecture":{"role":"collectionGroup","kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Architecture","abstract":[{"text":"공부 많이해서 상황에 따라 적절한 아키텍처를 선택해보자.","type":"text"}],"title":"Architecture","type":"topic","url":"\/documentation\/arinote\/architecture"},"https://i.imgur.com/40W9dJ6.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/40W9dJ6.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/40W9dJ6.png"}]},"https://i.imgur.com/fHrVIWZ.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/fHrVIWZ.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/fHrVIWZ.png"}]},"https://i.imgur.com/WwLGCRY.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/WwLGCRY.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/WwLGCRY.png"}]},"https://i.imgur.com/68mu4Em.png":{"type":"image","identifier":"https:\/\/i.imgur.com\/68mu4Em.png","alt":null,"variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/68mu4Em.png"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220520_urlsession,_nscache,_-_---.json b/docs/data/documentation/arinote/220520_urlsession,_nscache,_-_---.json new file mode 100644 index 0000000..68a9ea7 --- /dev/null +++ b/docs/data/documentation/arinote/220520_urlsession,_nscache,_-_---.json @@ -0,0 +1 @@ +{"metadata":{"roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}],"title":"220520 URLSession, NSCache, 셀 재사용"},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220520_URLSession,_NSCache,_-_---"},"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"sections":[],"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","type":"heading","anchor":"TIL-Today-I-Learned","level":1},{"inlineContent":[{"type":"text","text":"5월 20일 (금)"}],"type":"paragraph"},{"text":"학습 내용","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"이미지 캐싱 중 깜빡거리는 현상 문제 해결"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2,"type":"heading"},{"type":"aside","name":"Note","style":"note","content":[{"inlineContent":[{"type":"text","text":"서포터즈 활동 중, 두두와 사파리의 질문으로 나도 처음으로 겪은 문제를 기록하려고 한다."}],"type":"paragraph"}]},{"type":"heading","level":3,"text":"상황","anchor":"%EC%83%81%ED%99%A9"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/xH0xH3k.gif"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"cell을 엄!!!청!!! 빠르게 스크롤해서 내릴때"},{"text":" ","type":"text"},{"type":"text","text":"사라진 셀에서 시작된 이미지 다운로드가 멈추지 않아서"},{"type":"text","text":" "},{"type":"text","text":"이미지가 깜빡 거리는 현상이 나타나는 듯 보였음."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"즉 빠르게 스크롤 하게 되면,","type":"text"},{"type":"text","text":" "},{"text":"이미지 다운로드가 모두 멈추지않고 진행되면서","type":"text"},{"type":"text","text":" "},{"text":"이미지가 깜빡거리는 현상이 있었음.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이미지가 계속 다운로드 되면서 이미지뷰의 이미지를 계속 할당하는 현상."}]}]}]}]}],"type":"unorderedList"},{"type":"heading","level":1,"anchor":"","text":""},{"type":"heading","anchor":"%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"해결 방법","level":3},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"이미지 다운 받는 작업(Task)을 프로퍼티로 따로 선언해준다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"prepareForReuse()에서 Task를 중지, 취소를 하는 작업을 추가해준다. (suspend, cancel)"}]}]}]},{"syntax":"swift","type":"codeListing","code":["private var imageDownloadTask: URLSessionDataTask?","","private func downloadImage(imageURL: String?) {"," imageDownloadTask = ImageManager.shared.downloadImage(urlString: imageURL) { [weak self] result in"," switch result {"," case .success(let image):"," DispatchQueue.main.async {"," self?.thumbnailImageView.image = image"," }"," case .failure(_):"," break"," }"," }","}"]},{"syntax":"swift","code":["override func prepareForReuse() {"," super.prepareForReuse()"," \/\/ 문서에 중단된 작업들만 취소할 수 있다고 나와있음!"," imageDownloadTask?.suspend()"," imageDownloadTask?.cancel()","}"],"type":"codeListing"},{"type":"aside","style":"note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이렇게해서 이미지가 다운로드 중이여도 prepareForReuse가 호출된다면 작업이 중지, 취소되기 때문에 이미지가 깜빡거리는 현상을 해결할 수 있게 되었다."}]}],"name":"Note"},{"anchor":"","text":"","type":"heading","level":1},{"type":"heading","text":"소감","anchor":"%EC%86%8C%EA%B0%90","level":3},{"type":"paragraph","inlineContent":[{"type":"text","text":"그냥 의견 제시만 했을 뿐인데… 뚝딱 스무스하게 해결해버리셨다;;; 멋져부러…"},{"type":"text","text":" "},{"text":"나도 처음 겪는 문제라 메모해놔야징…ㅎ","type":"text"}]},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uitableviewcell\/1623223-prepareforreuse","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/foundation\/urlsessiontask\/1411565-suspend"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/foundation\/urlsessiontask\/1411591-cancel","type":"text"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"variants":[{"paths":["\/documentation\/arinote\/220520_urlsession,_nscache,_-_---"],"traits":[{"interfaceLanguage":"swift"}]}],"schemaVersion":{"minor":3,"major":0,"patch":0},"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/xH0xH3k.gif":{"identifier":"https:\/\/i.imgur.com\/xH0xH3k.gif","variants":[{"url":"https:\/\/i.imgur.com\/xH0xH3k.gif","traits":["1x","light"]}],"alt":"https:\/\/i.imgur.com\/xH0xH3k.gif","type":"image"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220523_api,_rest_api,_array,_set,_tuple,_library,_map.json b/docs/data/documentation/arinote/220523_api,_rest_api,_array,_set,_tuple,_library,_map.json new file mode 100644 index 0000000..cc5920b --- /dev/null +++ b/docs/data/documentation/arinote/220523_api,_rest_api,_array,_set,_tuple,_library,_map.json @@ -0,0 +1 @@ +{"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220523_API,_REST_API,_Array,_Set,_Tuple,_Library,_map"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/CS"]]},"schemaVersion":{"major":0,"minor":3,"patch":0},"kind":"article","sections":[],"metadata":{"roleHeading":"Article","modules":[{"name":"Ari's Notes"}],"title":"220523 API, REST API, Array, Set, Tuple, Library, map","role":"article"},"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","level":1,"anchor":"TIL-Today-I-Learned","type":"heading"},{"type":"paragraph","inlineContent":[{"text":"5월 23일 (금)","type":"text"}]},{"level":2,"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"면접 후, 제대로 답변 못한 부분 복기하기","type":"text"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"text":"고민한 점 \/ 해결 방법","type":"heading","level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[REST API vs API]"}],"type":"strong"}]},{"inlineContent":[{"type":"text","text":"면접 질문으로 나왔던 내용인데, 대답하지 못해서 나자신 다시 공부한다!!!"}],"type":"paragraph"},{"text":"REST API란?","anchor":"REST-API%EB%9E%80","level":3,"type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"애플리케이션과 서비스가 서로 통신하기 위한 일련의 규칙을 따르는 API","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"REST 아키텍처에 제약을 받기 때문에 RESTful API라고도 함.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"대규모 처리 기능 없이 유연한 방식으로 웹서비스에 액세스하는 방법을 제공한다."}],"type":"paragraph"}]}]},{"text":"Structure","type":"heading","anchor":"Structure","level":3},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"API는 응용 프로그램 형식을 따르지만 REST API는 클라이언트 및 서버의 웹 개념에서 동작한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"클라이언트와 서버가 서로 분리되어 더 많은 유연성을 제공한다."}],"type":"paragraph"}]}]},{"level":3,"text":"Design","anchor":"Design","type":"heading"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"API는 스마트폰과 같은 장치에 제한된 가제트용으로 설계된 경량 아키텍처이고, REST API는 시스템을 통해 통신하므로 복잡한 아키텍처이다."}],"type":"paragraph"}]}],"type":"unorderedList"},{"text":"Protocol","type":"heading","level":3,"anchor":"Protocol"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"API 주요 목표는 웹 서비스간의 데이터 교환을 표준화 하는 것. 따라서 API 유형에 따라 프로토콜 선택이 달라진다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"반면 REST API는 HTTP 프로토콜을 통해 상호 작용하는 웹 서비스를 구축하기 위한 아키텍처 스타일.","type":"text"}]}]}]},{"anchor":"Uniform-Interface","type":"heading","text":"Uniform Interface","level":3},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"클라이언트를 서버에서 분리하거나 애플리케이션을 다른 애플리케이션에서 분리하는 API는 많지 않다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"REST API는 균일한 인터페이스의 원칙을 준수하며, API내에서 자체 또는 다중 인터페이스 사용을 금지한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"Hypermedia 연결은 단일 인터페이스를 배포하는데 이상적으로 사용해야 한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"또한 유사한 데이터가 하나의 URI에만 속하도록 해야한다."}]}]},{"content":[{"inlineContent":[{"text":"결과적으로 초기 요청에 관계없이 동일한 리소스에 대한 모든 API요청은 동일하게 표시되어야 한다.","type":"text"}],"type":"paragraph"}]}]},{"anchor":"Scalability","text":"Scalability","type":"heading","level":3},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"일반 API의 경우 확장성이 문제인 반면 REST API는 계층 구조를 가지고 있어 REST API를 모듈화하고 보다 유연하게 만들어 확장성을 확보할 수 있다."}]}]}],"type":"unorderedList"},{"anchor":"%EA%B2%B0%EB%A1%A0%EC%9D%80-%EB%AD%90%EC%95%BC","type":"heading","text":"결론은 뭐야?","level":3},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"API는 더 광범위한 개념이고, REST API는 REST라는 규약을 따르는, 애플리케이션에서 널리 사용되는 API 유형이다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"REST API는 웹 API를 구축하는 방법에 대한 규칙\/표준\/지침의 집합이다.","type":"text"}]}]}]},{"anchor":"%ED%95%9C%EB%A7%88%EB%94%94%EB%A1%9C-%EC%A0%95%EB%A6%AC","level":3,"type":"heading","text":"한마디로 정리!"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"API는 기본적으로 한 응용 프로그램이 다른 응용 프로그램의 기능을 액세스할 수 있도록 하는 기능 및 절차의 집합입니다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"REST는 웹 API를 빌드하기 위한 일련의 규칙 또는 지침입니다. 기본적으로 클라이언트-서버 기반 응용 프로그램으로 제한되는 웹 상의 네트워크 응용 프로그램을 위한 아키텍처 스타일입니다.","type":"text"}]}]}]},{"level":1,"type":"heading","text":"","anchor":""},{"inlineContent":[{"type":"strong","inlineContent":[{"text":"[배열과 튜플, Set의 차이]","type":"text"}]}],"type":"paragraph"},{"text":"Array","level":3,"anchor":"Array","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"중복을 포함할 수 있는 값 모음이 필요하거나 항목의 순서가 중요한 경우 사용"}]}]}],"type":"unorderedList"},{"anchor":"Set","type":"heading","text":"Set","level":3},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"고유해야하는 값 모음이 필요하거나 특정 항목이 어디에 있는지 빠르게 확인할 수 있어야 하는 경우 사용"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"순서는 무시된다."}]}]}],"type":"unorderedList"},{"text":"Tuple","anchor":"Tuple","level":3,"type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"각 항목에 정확한 위치 또는 이름이 있는 특정 고정된 관련 값 컬렉션이 필요한 경우 사용","type":"text"}]}]}]},{"type":"heading","text":"Array, Set, Tuple은 언제 사용할까?","anchor":"Array-Set-Tuple%EC%9D%80-%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C","level":3},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"게임 사전에 모든 단어 목록을 저장하려면, 중복이 없고 순서가 중요하지 않으므로 Set"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"사용자가 읽은 모든 기사를 저장하려면 순서가 중요하지 않은 경우 Set, 순서가 중요하다면 배열","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"할 일 목록에 대한 항목을 저장하려면 순서를 예측할 수 있을 때 가장 잘 작동하므로 배열","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"정확히 두 개의 문자열, 정확히 두 개의 문자열과 정수, 또는 정확히 세개의 부울 또는 이와 유사한 것을 보유하려면 튜플을 사용한다."}]}]}]},{"type":"heading","level":1,"text":"","anchor":""},{"type":"paragraph","inlineContent":[{"type":"strong","inlineContent":[{"type":"text","text":"[라이브러리를 선정할 때 중요하게 생각해야하는 것]"}]}]},{"inlineContent":[{"type":"text","text":"나는 이것에 대한 기준이 아직 없는 것 같다. (경험 부족…)"},{"type":"text","text":" "},{"type":"text","text":"실제로 이 질문을 받았을 때, 대답을 시원하게 못한 것 같아서 주변 개발자에게 조언을 구해보았다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"text":"조언을 구해보았을 때 중요하게 봐야하는건 3가지 정도가 되는 것 같다.","type":"text"}]},{"text":"안정적인 라이브러리인지?","level":3,"anchor":"%EC%95%88%EC%A0%95%EC%A0%81%EC%9D%B8-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC%EC%9D%B8%EC%A7%80","type":"heading"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"보통 라이브러리가 나온지 오래되었고, 이슈를 통해 버그나 에러사항들을 고쳐서 안정적으로 돌아가는 라이브러리들이 있다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"하지만 몇년째 이슈가 해결이 안되서 버그가 고쳐지지 않고 있는 라이브러리도 있다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 사용하기 전에 크리티컬한 이슈나 버그들은 없는지 꼭 확인해야한다."}]}]}],"type":"unorderedList"},{"type":"heading","anchor":"%EC%82%AC%EC%9A%A9%EC%9E%90%EA%B0%80-%EB%A7%8E%EC%95%84%EC%84%9C-%EC%98%A4%ED%94%88%EC%86%8C%EC%8A%A4-%EB%A0%88%ED%8C%8C%EC%A7%80%ED%86%A0%EB%A6%AC%EA%B0%80-%ED%99%9C%EB%B0%9C%ED%95%9C%EC%A7%80","level":3,"text":"사용자가 많아서 오픈소스 레파지토리가 활발한지?"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자들이 많다는 것은 그만큼 오픈소스가 활발하다는 뜻이다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"활발하다면 이미 발견된 버그나 에러사항들을 해결했다는 뜻이기도 하다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"또한 문제가 생겼을 때에도 봐줄 개발자들이 많기 때문에 해결도 수월해진다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 사용자가 많고 활발한 오픈소스인지도 중요하게 봐야할 요소중 하나이다."}]}]}],"type":"unorderedList"},{"type":"heading","anchor":"%EB%9D%BC%EC%9D%B4%EC%84%BC%EC%8A%A4%EA%B0%80-%EC%A0%81%EC%A0%88%ED%95%9C%EC%A7%80","level":3,"text":"라이센스가 적절한지?"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"개인 프로젝트를 할 경우에는 이 라이센스를 중요하게 보지 않는다고 한다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"하지만 회사의 경우는 라이센스에 치명적인 규약들이 있는지 없는지 꼭 검토를 해야한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"만약 오픈소스를 가져다 썼는데, 라이센스 조건이 가져다 쓰게되면 해당 라이센스를 따라야하며, 코드를 공개해야하고, 금전적인 이득을 취할 수 없다는 내용이 있다면…. 회사에서는 무조건 그 라이브러리는 쓸 수 없게 되겠지…"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"만약 실수로 업무중에 그런 라이센스가 포함되어있는 라이브러리를 가져다썼다고 하면…. 상상만 해도 싫네…."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 라이센스에 크리티컬한 규약은 없는지 꼭!!! 중요하게 살펴봐야한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"이런 내용을 바탕으로 실제 사례로 구글에서 자바 저작권 문제로 10년째 소송을 하고 있다고 한다… 조심해야지 😭"}],"type":"paragraph"}]}]},{"text":"","anchor":"","type":"heading","level":1},{"type":"paragraph","inlineContent":[{"inlineContent":[{"type":"text","text":"[map]"}],"type":"strong"}]},{"type":"paragraph","inlineContent":[{"text":"맵에 대해서 설명해주세요 라는 질문을 받았는데,","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"어떻게 동작하는지 작동방식에 대해서 이해는 하고 있지만"},{"text":" ","type":"text"},{"type":"text","text":"말로는 설명을 시원하게 못하니까, 난 모르는거야…. 그래서 복기한다!!!!!"},{"text":" ","type":"text"},{"text":"(중구난방으로 설명을 힘들게 했던….)","type":"text"}]},{"level":3,"text":"Map","anchor":"Map","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"맵은 데이터를 변형하고자 할 때 사용한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기존 데이터를 변형하여 새로운 컨테이너를 만드는데, 기존 데이터는 변형되지 않는다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"특히 맵은 기존의 for-in 구문과 큰 차이는 없지만 다음과 같은 이점이 있다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"코드의 간결성"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"재사용 용이","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"컴파일러 최적화 성능 좋음"}],"type":"paragraph"}]}]}]}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/stackoverflow.com\/questions\/41189842\/what-is-difference-between-rest-and-api"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/hevodata.com\/learn\/api-vs-rest-api\/"}]}]}],"type":"unorderedList"}]}]}]}],"variants":[{"paths":["\/documentation\/arinote\/220523_api,_rest_api,_array,_set,_tuple,_library,_map"],"traits":[{"interfaceLanguage":"swift"}]}],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/CS":{"type":"topic","role":"collectionGroup","url":"\/documentation\/arinote\/cs","kind":"article","title":"CS","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/CS","abstract":[{"type":"text","text":"Computer Science"}]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220613_github_actions,_ci,_cd,_---.json b/docs/data/documentation/arinote/220613_github_actions,_ci,_cd,_---.json new file mode 100644 index 0000000..496bd74 --- /dev/null +++ b/docs/data/documentation/arinote/220613_github_actions,_ci,_cd,_---.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Git"]]},"sections":[],"kind":"article","schemaVersion":{"minor":3,"patch":0,"major":0},"variants":[{"paths":["\/documentation\/arinote\/220613_github_actions,_ci,_cd,_---"],"traits":[{"interfaceLanguage":"swift"}]}],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220613_Github_Actions,_CI,_CD,_---"},"metadata":{"roleHeading":"Article","role":"article","title":"220613 Github Actions, CI, CD, 자동화","modules":[{"name":"Ari's Notes"}]},"primaryContentSections":[{"kind":"content","content":[{"type":"heading","level":1,"text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned"},{"inlineContent":[{"text":"6월 13일 (월)","type":"text"}],"type":"paragraph"},{"text":"학습 내용","type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Github Actions로 자동화된 작업을 진행할 수 있도록 만들어보기"}],"type":"paragraph"}]}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","type":"heading"},{"content":[{"inlineContent":[{"text":"요즘 Toss에서 개발자 컨퍼런스가 올라와서 관심있게 보고있는데, 그 중 자동화 언급이 되었던 부분을 내 개인 프로젝트에서도 한번 적용해보면 재밌겠다 싶어서 빌드 및 테스트를 자동화하는 방법을 찾아보았다.","type":"text"}],"type":"paragraph"}],"name":"Note","type":"aside","style":"note"},{"inlineContent":[{"inlineContent":[{"text":"[Github Action]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"소프트웨어 개발 라이프 사이클안에서 PR, Push 등의 이벤트 발생에 따라 자동화된 작업을 진행할 수 있게 해주는 기능이다."}]},{"inlineContent":[{"type":"text","text":"이런 자동화된 작업이 필요한 경우는 어떤 것들이 존재하는지 알아보자."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"CI\/CD","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Github Actions을 활용하는 가장 대표적인 예시 중 하나","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"로컬 레포지토리에서 원격 레포지토리로 푸쉬하고 난 후, Github Actions에서는 이벤트 발생에 따라 자동으로 빌드 및 배포하는 스크립트를 실행시켜주는 것"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"애플리케이션의 규모가 클 수록 빌드, 배포 시간이 오래걸리는데 이를 자동화 시켜놓으면 해당 시간을 낭비하지 않을 수 있다."}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Testing"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"팀 프로젝트를 진행하다가 PR를 보내면 자동으로 테스트를 진행하는 것 또한 Github Actions으로 구현할 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"테스트 성공 여부에 따라서 자동으로 PR을 Open 및 Close를 할 수 있다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"Cron Job"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Github Actions를 통해 특정 시간대에 스크립트를 반복 실행하도록 구현할 수 있다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"매일 특정 시간이 되면 크롤링 작업을 진행한다는 등의 예시가 존재한다."}],"type":"paragraph"}]}]}]}],"type":"orderedList"},{"type":"heading","anchor":"Github-Actions%EC%9D%98-%EA%B5%AC%EC%84%B1-%EC%9A%94%EC%86%8C","text":"Github Actions의 구성 요소","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"Workflow","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"레파지토리에 추가할 수 있는 일련의 자동화된 커맨드 집합이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"하나 이상의 job으로 구성되어 있으며, push나 PR 같은 이벤트에 의해 실행될 수도 있고 특정 시간대에 실행될 수도 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"빌드, 테스트, 배포 등 각각의 역할에 맞는 workflow를 추가할 수 있고, ","type":"text"},{"type":"codeVoice","code":".github\/workflows"},{"text":" 디렉토리에 .yml 형태로 저장한다.","type":"text"}],"type":"paragraph"}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"Event","type":"codeVoice"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"Workflow를 실행시키는 push, PR, commit 등의 특정 행동을 의미한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"위 특정 행동이 아닌, ","type":"text"},{"type":"reference","identifier":"https:\/\/docs.github.com\/en\/rest\/repos\/repos#create-a-repository-dispatch-event","isActive":true},{"text":"을 사용하면 Github 외부에서 발생한 이벤트에 의해서도 workflow를 실행시킬 수 있다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이벤트의 종류는 "},{"identifier":"https:\/\/docs.github.com\/en\/actions\/using-workflows\/events-that-trigger-workflows","isActive":true,"type":"reference"},{"type":"text","text":"에서 확인 가능하다."}]}]}]}]},{"content":[{"inlineContent":[{"code":"Job","type":"codeVoice"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"동일한 Runner에서 실행되는 여러 Step의 집합을 의미한다."}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기본적으로 하나의 workflow 내의 여러 job은 독립적으로 실행되지만 필요에 따라 의존 관계를 설정하여 순서를 지정해줄 수 있다."}]}]},{"content":[{"inlineContent":[{"text":"예시로 테스트를 수행하는 Job과 빌드 작업을 수행하는 Job이 하나의 workflow에 존재한다고 생각해보면 여기서 테스트 job은 반드시 빌드 job 이후에 수행되어야 하는데, 여기서 의존관계를 설정해 빌드 job이 성공적으로 끝나야 테스트 job을 수행할 수 있도록 지정할 수 있다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"따라서 만약 빌드가 실패할 시에는 테스트 job도 실행되지 않는다."}]}]}],"type":"unorderedList"}]}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"Step"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"Github Actions workflow 내에 있는 Job을 실행시키기 위한 어플리케이션.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Runner Application은 Github에서 호스팅하는 가상 환경 또는 직접 호스팅하는 가상환경에서 실행가능하며, Github에서 호스팅하는 가상 인스턴스의 겅우 메모리 및 용량 제한이 존재한다."}]}]}]}]}],"type":"unorderedList"},{"anchor":"Github%EC%97%90%EC%84%9C-%EC%A0%9C%EA%B3%B5%ED%95%98%EB%8A%94-yml-%ED%85%9C%ED%94%8C%EB%A6%BF","type":"heading","level":2,"text":"Github에서 제공하는 yml 템플릿"},{"code":["# Repository의 Actions 탭에 나타날 Workflow 이름으로 필수 옵션은 아닙니다.","name: CI","","# Workflow를 실행시키기 위한 Event 목록입니다.","on:"," # 하단 코드에 따라 develop 브랜치에 Push 또는 Pull Request 이벤트가 발생한 경우에 Workflow가 실행됩니다."," # 만약 브랜치 구분 없이 이벤트를 지정하고 싶을 경우에는 단순히 아래와 같이 작성도 가능합니다."," # on: [push, pull_request]"," push:"," branches: [develop]"," pull_request:"," branches: [develop]",""," # 해당 옵션을 통해 사용자가 직접 Actions 탭에서 Workflow를 실행시킬 수 있습니다."," # 여기에서는 추가적으로 더 설명하지는 않겠습니다."," workflow_dispatch:","","# 해당 Workflow의 하나 이상의 Job 목록입니다.","jobs:"," # Job 이름으로, build라는 이름으로 Job이 표시됩니다."," build:"," # Runner가 실행되는 환경을 정의하는 부분입니다."," runs-on: ubuntu-latest",""," # build Job 내의 step 목록입니다."," steps:"," # uses 키워드를 통해 Action을 불러올 수 있습니다."," # 여기에서는 해당 레포지토리로 check-out하여 레포지토리에 접근할 수 있는 Action을 불러왔습니다."," - uses: actions\/checkout@v2",""," # 여기서 실행되는 커맨드에 대한 설명으로, Workflow에 표시됩니다."," - name: Run a one-line script"," run: echo Hello, world!",""," # 이렇게 하나의 커맨드가 아닌 여러 커맨드도 실행 가능합니다."," - name: Run a multi-line script"," run: |"," echo Add other actions to build,"," echo test, and deploy your project."],"syntax":"yaml","type":"codeListing"},{"text":"","level":1,"type":"heading","anchor":""},{"inlineContent":[{"inlineContent":[{"text":"[빌드 및 테스트를 자동화하는 workflow를 등록하는 방법]","type":"text"}],"type":"strong"}],"type":"paragraph"},{"inlineContent":[{"identifier":"https:\/\/i.imgur.com\/bstRdNW.png","type":"image"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"먼저 내 레파지토리에 들어가서 Actions 탭에 들어가 ","type":"text"},{"code":"New workflow","type":"codeVoice"},{"type":"text","text":" 버튼을 클릭!"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/Qe6Pr4b.png","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"code":"Set up a workflow youself","type":"codeVoice"},{"text":"를 누르면 템플릿이 적용된 yml 파일을 생성할 수 있다.","type":"text"}],"type":"paragraph"}]}]},{"type":"codeListing","syntax":"yaml","code":["# name을 테스트 실행으로 바꾸어주었다.","name: Run Test ","","on:"," # develop 브랜치에 push 나 pull request 이벤트가 일어났을때 해당 workflow 를 trigger"," push:"," branches: [ \"develop\" ]"," pull_request:"," branches: [ \"develop\" ]",""," workflow_dispatch:","","# workflow의 실행은 하나 이상의 job으로 구성 됨","jobs:"," # 이 workflow 는 \"build\" 라는 single job 으로 구성"," build:"," # job이 실행될 환경 - 최신 mac os"," runs-on: macos-latest",""," # Steps은 job의 일부로 실행될 일련의 task들을 나타냄"," steps:"," # uses 키워드를 통해 Github Actions에서 기본으로 제공하는 액션을 사용 가능. 아래 액션은 repository 에 체크아웃하는 것"," - uses: actions\/checkout@v3",""," # shell 이용해서 하나의 command 수행"," - name: Start xcode build 🛠"," run: |"," xcodebuild clean test -project Animal-Crossing-Wiki\/Animal-Crossing-Wiki.xcodeproj -scheme Animal-Crossing-Wiki -destination 'platform=iOS Simulator,name=iPhone 13 Pro,OS=15.2'"]},{"inlineContent":[{"type":"text","text":"마지막으로 작성된 run 부분은 "},{"type":"codeVoice","code":"xcodebulid command"},{"type":"text","text":"를 사용한 것인데 좀 더 자세히 살펴보자 !"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"Xcodebulid command","type":"codeVoice"},{"type":"text","text":"란?"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Xcode 프로젝트 및 workspace의 build, query, analyze, test, archive 작업을 수행할 수 있는 command를 뜻한다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"사용할 수 있는 다양한 action 및 option은 man page("},{"code":"x-man-page:\/\/xcodebuild","type":"codeVoice"},{"text":") 에서 확인 가능하다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"name":"Note","content":[{"inlineContent":[{"type":"text","text":"크롬 주소창에 "},{"code":"x-man-page:\/\/xcodebuild","type":"codeVoice"},{"text":"을 입력하면 해당 창이 나타난다.","type":"text"}],"type":"paragraph"}],"style":"note","type":"aside"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/iipkyu0.png"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"위에서 사용한 Action"}]},{"items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"clean"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"build products 및 intermediate 파일을 build root(SINROOT)에서 제거","type":"text"}]}]}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"test"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"build root(SINROOT)에서 scheme을 테스트(빌드가 성공적으로 진행된 후에 테스트가 진행됨). scheme와 destination 지정이 필요하다."}]}]}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":"Option을 하나씩 살펴보자."}]},{"type":"orderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"우선 액션(clean, test) 뒤에 "},{"type":"codeVoice","code":"-project"},{"text":"라는 플래그 뒤에 ","type":"text"},{"code":".xcodeproj","type":"codeVoice"},{"text":" 파일명을 확장자까지 입력한다. (경로 주의)","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"xcodebuild clean test -project Animal-Crossing-Wiki\/Animal-Crossing-Wiki.xcodeproj","type":"codeVoice"}]}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"스킴 설정을 위해 "},{"type":"codeVoice","code":"-sche"},{"text":"me 플래그와 함께 빌드하고자 하는 scheme을 명시해준다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"-scheme Animal-Crossing-Wiki"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"code":"-destination","type":"codeVoice"},{"text":" 플래그를 사용하여 빌드시 사용할 플랫폼, 기기명, iOS 버전을 명시해준다.","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"codeVoice","code":"-destination 'platform=iOS Simulator,name=iPhone 13 Pro,OS=15.2'"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"name":"Note","content":[{"inlineContent":[{"type":"text","text":"풀로 설정된 명령어"}],"type":"paragraph"}],"type":"aside","style":"note"},{"syntax":null,"code":["xcodebuild clean test -project Animal-Crossing-Wiki\/Animal-Crossing-Wiki.xcodeproj -scheme Animal-Crossing-Wiki -destination 'platform=iOS Simulator,name=iPhone 13 Pro,OS=15.2'"],"type":"codeListing"},{"inlineContent":[{"type":"text","text":"이렇게 .yml파일을 설정해주고, develop 브랜치에서 push를 하게 되면, 아까 Actions 탭에 작성해두었던 workflow가 트리거 되어 실행된다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/9d77sXy.png"}]},{"type":"aside","name":"사진에서 보면 ‘Run Test’라는 이름이 yml 파일에서 가장 상위에 name","style":"note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Run Test로 작성해줬던 값과 동일한 것을 확인할 수 있다."}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/ji5485.github.io\/post\/2021-06-06\/build-ci-cd-pipeline-using-github-actions\/","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/sujinnaljin.medium.com\/ci-cd-github-actions-%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-xcode-build-test-%EC%9E%90%EB%8F%99%ED%99%94-73b90a3dcc65","type":"text"}]}]}]}]}],"type":"unorderedList"}]}],"references":{"https://i.imgur.com/9d77sXy.png":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/9d77sXy.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/9d77sXy.png"}]},"https://i.imgur.com/Qe6Pr4b.png":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/Qe6Pr4b.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/Qe6Pr4b.png"}]},"doc://AriNote/documentation/AriNote/Git":{"abstract":[{"type":"text","text":"Git 관련 지식"}],"type":"topic","title":"Git","url":"\/documentation\/arinote\/git","kind":"article","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Git"},"https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows":{"type":"link","url":"https:\/\/docs.github.com\/en\/actions\/using-workflows\/events-that-trigger-workflows","title":"Github Actions 공식문서","titleInlineContent":[{"text":"Github Actions 공식문서","type":"text"}],"identifier":"https:\/\/docs.github.com\/en\/actions\/using-workflows\/events-that-trigger-workflows"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/bstRdNW.png":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/bstRdNW.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/bstRdNW.png"}]},"https://i.imgur.com/iipkyu0.png":{"type":"image","alt":null,"identifier":"https:\/\/i.imgur.com\/iipkyu0.png","variants":[{"traits":["1x","light"],"url":"https:\/\/i.imgur.com\/iipkyu0.png"}]},"https://docs.github.com/en/rest/repos/repos#create-a-repository-dispatch-event":{"type":"link","url":"https:\/\/docs.github.com\/en\/rest\/repos\/repos#create-a-repository-dispatch-event","title":"Repository Dispatch Webhook","titleInlineContent":[{"text":"Repository Dispatch Webhook","type":"text"}],"identifier":"https:\/\/docs.github.com\/en\/rest\/repos\/repos#create-a-repository-dispatch-event"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220711_arc,_dismiss,_popviewcontroller,_memory_leak.json b/docs/data/documentation/arinote/220711_arc,_dismiss,_popviewcontroller,_memory_leak.json new file mode 100644 index 0000000..567e6bc --- /dev/null +++ b/docs/data/documentation/arinote/220711_arc,_dismiss,_popviewcontroller,_memory_leak.json @@ -0,0 +1 @@ +{"sections":[],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/220711_arc,_dismiss,_popviewcontroller,_memory_leak"]}],"schemaVersion":{"major":0,"minor":3,"patch":0},"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","level":1,"type":"heading","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"text":"7월 11일 (월)","type":"text"}]},{"level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading","text":"학습 내용"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"ViewController를 ","type":"text"},{"code":"dismiss","type":"codeVoice"},{"type":"text","text":", 혹은 "},{"code":"popViewController","type":"codeVoice"},{"text":" 했을 때, 메모리 할당이 해제 되지 않는 이유가 뭘까?","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"heading","text":"고민한 점 \/ 해결 방법","level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"개인 프로젝트를 하던 와중에 Coordinator 패턴을 활용하여 화면 제어를 하고 있었다. 그러다가 간혹 dismiss, popViewController를 시도하면 해당 뷰 컨트롤러가 메모리에서 사라져야 하는데, 죽지않고 살아남아있는 경우가 발생하는 걸 발견해버렸다. 이를 해결하려고 방법을 찾아보았다.","type":"text"}]}],"style":"note","type":"aside"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/gar9H4P.gif","type":"image"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"첫번째 시도"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뷰가 사라질 때 ViewController가 가지고 있는 ViewModel을 nil처리를 해주어보았다."}]}]},{"content":[{"inlineContent":[{"text":"하지만 효과는 없었다. 이 문제가 아닌 것 같았다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"code":"두번째 시도","type":"codeVoice"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"방법을 모색하던 와중에, 해당 내용에 대한 stackoverflow 질문 글을 찾게 되었다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"뷰 컨트롤러를 닫거나, 팝할 때 강력한 참조를 만들지 않은 경우에만 할당이 해제된다는 답변이 있었다.","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}]},{"inlineContent":[{"type":"text","text":"따라서 "},{"isActive":true,"type":"reference","identifier":"https:\/\/stackoverflow.com\/a\/21621174\/19072322"},{"text":"을 참고하여, 문제가 발생하는 뷰 컨트롤러에 강한 참조를 하고 있는 부분이 있나 체크를 해보았다.","type":"text"}],"type":"paragraph"},{"type":"aside","style":"note","name":"Note","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"위에서 말했던 문제가 발생하던 뷰 컨트롤러의 수가 적지 않았다. 살펴보니 대부분 바인딩 처리해주는 부분에서 옵저버블을 구독하는 시점에 self를 약한 참조로 사용하지 않고 강한 참조로 사용하던 부분 때문에 발생하던 문제였다."}]}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"해결 방법"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"뷰 바인딩 처리해주는 부분에서, 혹은 ViewController 내부에서 self를 사용하는 구간을 모두 약한 참조를 하도록 바꾸어주었다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"그리고 viewModel을 뷰 컨트롤러가 소유했던 방향을 "},{"code":"bind(to:)","type":"codeVoice"},{"type":"text","text":" 라는 메소드로 변경하여 "},{"code":"파라미터로 뷰모델을 전달받아 바인딩 처리","type":"codeVoice"},{"type":"text","text":"를 하도록 구조를 수정해주었다."}],"type":"paragraph"}]}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/i.imgur.com\/QFFaMwa.gif"}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/stackoverflow.com\/questions\/21398934\/why-arc-is-not-deallocating-memory-after-popviewcontroller","type":"text"}]}]}]}]}]}]}],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/220711_ARC,_dismiss,_popViewController,_memory_leak"},"metadata":{"role":"article","roleHeading":"Article","title":"220711 ARC, dismiss, popViewController, memory leak","modules":[{"name":"Ari's Notes"}]},"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"references":{"https://i.imgur.com/gar9H4P.gif":{"alt":null,"identifier":"https:\/\/i.imgur.com\/gar9H4P.gif","type":"image","variants":[{"url":"https:\/\/i.imgur.com\/gar9H4P.gif","traits":["1x","light"]}]},"https://stackoverflow.com/a/21621174/19072322":{"identifier":"https:\/\/stackoverflow.com\/a\/21621174\/19072322","titleInlineContent":[{"type":"text","text":"이 답변"}],"url":"https:\/\/stackoverflow.com\/a\/21621174\/19072322","title":"이 답변","type":"link"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://i.imgur.com/QFFaMwa.gif":{"alt":null,"identifier":"https:\/\/i.imgur.com\/QFFaMwa.gif","type":"image","variants":[{"url":"https:\/\/i.imgur.com\/QFFaMwa.gif","traits":["1x","light"]}]},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220813_rxswift,_almofire,_urlprotocol,_unit_test.json b/docs/data/documentation/arinote/220813_rxswift,_almofire,_urlprotocol,_unit_test.json new file mode 100644 index 0000000..9981140 --- /dev/null +++ b/docs/data/documentation/arinote/220813_rxswift,_almofire,_urlprotocol,_unit_test.json @@ -0,0 +1 @@ +{"variants":[{"paths":["\/documentation\/arinote\/220813_rxswift,_almofire,_urlprotocol,_unit_test"],"traits":[{"interfaceLanguage":"swift"}]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220813_RxSwift,_Almofire,_URLProtocol,_Unit_Test","interfaceLanguage":"swift"},"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Rx"]]},"schemaVersion":{"major":0,"minor":3,"patch":0},"sections":[],"metadata":{"roleHeading":"Article","title":"220813 RxSwift, Almofire, URLProtocol, Unit Test","modules":[{"name":"Ari's Notes"}],"role":"article"},"primaryContentSections":[{"kind":"content","content":[{"text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned","type":"heading","level":1},{"inlineContent":[{"type":"text","text":"8월 13일 (토)"}],"type":"paragraph"},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"type":"heading","text":"학습 내용"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Almofire를 Moking해서 테스트 해보기 도전!"}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"type":"heading","level":2,"text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"type":"paragraph","inlineContent":[{"type":"text","text":"블로그에 간만에 포스팅했다."},{"type":"text","text":" "},{"type":"text","text":"https:\/\/leeari95.tistory.com\/71"}]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/foundation\/urlprotocol"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/jeroenscode.com\/mocking-alamofire\/","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/developer.apple.com\/videos\/play\/wwdc2018\/417\/","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/yagom.net\/courses\/unit-test-%ec%9e%91%ec%84%b1%ed%95%98%ea%b8%b0\/"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"}]}],"references":{"doc://AriNote/documentation/AriNote/Rx":{"type":"topic","abstract":[{"type":"text","text":"RxSwift를 활용한 비동기 프로그래밍"}],"role":"collectionGroup","url":"\/documentation\/arinote\/rx","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Rx","title":"RxSwift","kind":"article"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/220923_urlsession,_cachepolicy.json b/docs/data/documentation/arinote/220923_urlsession,_cachepolicy.json new file mode 100644 index 0000000..0fb8d80 --- /dev/null +++ b/docs/data/documentation/arinote/220923_urlsession,_cachepolicy.json @@ -0,0 +1 @@ +{"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/220923_URLSession,_CachePolicy","interfaceLanguage":"swift"},"schemaVersion":{"major":0,"minor":3,"patch":0},"variants":[{"paths":["\/documentation\/arinote\/220923_urlsession,_cachepolicy"],"traits":[{"interfaceLanguage":"swift"}]}],"metadata":{"roleHeading":"Article","role":"article","title":"220923 URLSession, CachePolicy","modules":[{"name":"Ari's Notes"}]},"sections":[],"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Swift"]]},"primaryContentSections":[{"content":[{"type":"heading","text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned","level":1},{"type":"paragraph","inlineContent":[{"text":"9월 23일 (금)","type":"text"}]},{"text":"학습 내용","type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"네트워크 요청 시 Response 캐시 적용 하지 않는 방법 ("},{"code":"문제 해결","type":"codeVoice"},{"text":")","type":"text"}]}]}]},{"inlineContent":[{"text":" ","type":"text"}],"type":"paragraph"},{"type":"heading","text":"고민한 점 \/ 해결 방법","level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"content":[{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/i.imgur.com\/4EWNm90.gif","type":"image"}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"스타 체크 후 새로고침 시 이전 데이터를 불러와서 제대로 업데이트 되지 않는 현상"}]}],"style":"note","type":"aside","name":"Note"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"code":"문제 상황","type":"codeVoice"},{"text":" Github API로 스타표시를 한 후, 사용자의 스타 체크한 레파지토리 목록을 불러올 때, 업데이트 된 목록이 아니라 기존에 불러왔었던 Response를 불러오는 문제로 레파지토리 목록이 업데이트가 제대로 되지 않는 현상이였다. (PUT > GET)","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"추측해보기","type":"codeVoice"},{"type":"text","text":" 왜 PUT 이후 GET을 요청했을 때, 업데이트 된 레파지토리 목록을 불러오는데 시간이 걸릴까? > API 자체적인 문제인가? 포스트맨으로도 테스트 해보자! > 정상이다. 혹시 리스폰스를 캐시처리 하나…?"}]}]}]}]},{"content":[{"inlineContent":[{"code":"이유","type":"codeVoice"},{"text":" 내 생각에는 기존에 요청해서 받았던 리스폰스가 캐싱되어 새롭게 업데이트 된 데이터를 불러오는 것이 아니라 캐시처리 되어있는 리스폰스를 불러오는 것 같다는 생각이 들었다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"첫 번째 시도"},{"type":"text","text":" URLSessionConfiguration의 속성을 ephemeral로 할당한 후 테스트 해본 결과, 정상적으로 새롭게 업데이트 된 레파지토리를 받아왔다."}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"하지만 이걸로 해결되었다고 결론내기가 찝찝했다. 그 이유는 ephemeral 속성의 경우 다른 네트워크 요청들도 캐시가 적용되지 않는다는 점이다. 이미지의 경우는 캐시처리가 되는 것이 더 도움이 될탠데… 하는 생각이 들어서 특정 요청만 캐시처리를 하지 않도록 하고 싶었다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"그래서 다시 찾아본 결과 URLRequest 내부 속성중에는 캐시정책을 설정할 수 있는 속성이 있었다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/foundation\/nsurlrequest\/cachepolicy"}],"type":"paragraph"}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"또한 아예 헤더에 Cache-Control을 할당해서 리스폰스를 캐시하지 않겠다고 하고 네트워크를 요청할 수도 있다."}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/www.blog-dreamus.com\/post\/cache-control-%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B4%EC%9C%A0"}]}]}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"해결 방법"},{"text":" 위와 같이 URLRequest 속성중 ","type":"text"},{"code":"cachePolicy","type":"codeVoice"},{"type":"text","text":"를 "},{"code":".reloadIgnoringLocalCacheData","type":"codeVoice"},{"type":"text","text":"로 할당해주어서 해결하였다. 헤더에 Cache-Control를 할당주어 데이터를 요청 시에 Cache 관련 제어를 하는 방법도 있지만 하드코딩이라는 생각이 들어서 이러한 방법으로 해결하게 되었다."}],"type":"paragraph"}]}]},{"name":"Note","style":"note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Header를 통한 해결 방법"}]}]},{"syntax":"swift","type":"codeListing","code":["var headers: [String : String]? {"," return ["," \"Authorization\": \"token \\(KeyChainManager.shard.load(GithubAuthorize.token) ?? \"\")\","," \"Cache-Control\": \"no-store\" \/\/ 이 부분을 추가하여 리스폰스를 캐시처리 하지 않도록 요청"," ]"," }"]},{"name":"Note","content":[{"inlineContent":[{"text":"URLRequest의 프로퍼티를 이용한 해결 방법","type":"text"}],"type":"paragraph"}],"style":"note","type":"aside"},{"type":"codeListing","code":["var request = URLRequest(url: url)","","\/\/ 캐시 정책 할당 (로컬 캐시 데이터 무시)","request.cachePolicy = .reloadIgnoringLocalCacheData "],"syntax":"swift"},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/foundation\/urlsessionconfiguration","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/foundation\/nsurlrequest\/cachepolicy"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/medium.nextlevelswift.com\/urlrequest-cache-policy-f7c30a96b698"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/www.blog-dreamus.com\/post\/cache-control-%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B4%EC%9C%A0"}]}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/Swift":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","role":"collectionGroup","abstract":[{"type":"text","text":"언어 공부"}],"kind":"article","title":"Swift","url":"\/documentation\/arinote\/swift","type":"topic"},"https://i.imgur.com/4EWNm90.gif":{"variants":[{"url":"https:\/\/i.imgur.com\/4EWNm90.gif","traits":["1x","light"]}],"alt":null,"type":"image","identifier":"https:\/\/i.imgur.com\/4EWNm90.gif"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/230710_addsubview_referencecount.json b/docs/data/documentation/arinote/230710_addsubview_referencecount.json new file mode 100644 index 0000000..d8f5374 --- /dev/null +++ b/docs/data/documentation/arinote/230710_addsubview_referencecount.json @@ -0,0 +1 @@ +{"sections":[],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"kind":"article","schemaVersion":{"minor":3,"major":0,"patch":0},"primaryContentSections":[{"content":[{"type":"heading","level":1,"text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned"},{"type":"paragraph","inlineContent":[{"type":"text","text":"7월 10일 (월)"}]},{"text":"학습 내용","type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"addSubview 시 참조 카운트에 대한 것","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"text":"고민한 점 \/ 해결 방법","type":"heading","level":2,"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95"},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"메모리 누수가 있어 문제를 해결하다가… 프로퍼티로 선언된 뷰를 addSubview를 하게 된다면 Reference가 어떻게 카운트 되는지 갑자기 궁금해져서 더 자세히 알고 싶었다."}]}],"name":"Note","style":"note","type":"aside"},{"type":"paragraph","inlineContent":[{"type":"text","text":"공식문서를 살펴보면…"}]},{"inlineContent":[{"identifier":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/4602d6e6-7981-47e7-88d1-11a4a46f73ff","type":"image"}],"type":"paragraph"},{"name":"Note","type":"aside","content":[{"inlineContent":[{"type":"text","text":"addSubview로 추가한 뷰를 강한 참조로 카운트 된다고 설명하고 있다."}],"type":"paragraph"}],"style":"note"},{"type":"paragraph","inlineContent":[{"text":"그렇다면 평소에 뷰를 프로퍼티로 선언할 때, 약한 참조로 설정해야할 때는 어떤 경우일까?","type":"text"}]},{"type":"paragraph","inlineContent":[{"text":"찾아보니 아래와 같은 경우라면 ","type":"text"},{"type":"codeVoice","code":"weak"},{"text":"로 선언하는 것이 좋다고 대부분 이야기한다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"뷰가 런타임에 사라질 수 있는 경우"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":"반대로 뷰가 항상 view hierarchy에 존재한다면 strong으로 선언해도 아무 상관이 없다는 뜻이다."},{"text":" ","type":"text"},{"type":"text","text":"superView가 메모리에서 해제되면 subView도 메모리에서 해제되기 때문이다."}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"하지만 ViewController의 view에 addSubview를 하고, 런타임 때 subView를 removeFromSuperView() 메소드를 호출하여 화면에서도 제거하고 메모리에서도 제거하고 싶다면 "},{"code":"weak","type":"codeVoice"},{"type":"text","text":"로 선언해야 한다."},{"text":" ","type":"text"}]},{"inlineContent":[{"type":"text","text":"그 이유는, ViewController의 view는 뷰 컨트롤러가 메모리에서 해제될 때 같이 해제되기 때문이다."},{"text":" ","type":"text"},{"type":"text","text":"즉, view가 런타임 때 해제되지 않는다면 그 하위뷰도 ViewController에서 강한 참조로 유지되고 있기 때문에 removeFromSuperView()를 호출해도 화면에서만 사라지고 메모리는 해제되지 않는다."},{"text":" ","type":"text"}],"type":"paragraph"},{"inlineContent":[{"text":"이러한 이유로 프로퍼티로 뷰를 선언할 때 weak로 선언하는 것을 대부분 선호하는 것 같다.","type":"text"},{"type":"text","text":" "},{"type":"text","text":"weak로 선언해두면 removeFromSuperView()를 호출했을 때 더이상 참조카운트가 존재하지 않기 때문에 subView가 스스로 메모리에서 해제될 수 있게 된다."}],"type":"paragraph"},{"inlineContent":[{"text":"정리하자면…","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"ViewController 내부에 뷰를 선언할 때 강한 참조든, 약한 참조든 상관없다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"강한 참조, 약한 참조로 선언하는 것에 대한 차이점은 해당 뷰가 스스로 메모리에서 해제될 수 있느냐 없느냐가 포인트다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"따라서 view hierarchy에 아예 사라지는 시점이 존재한다면 weak로 선언해주는 것이 자연스럽다."}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사라지는 시점에 바로 메모리가 해제되기 때문이다."}]}]}],"type":"unorderedList"}]},{"content":[{"inlineContent":[{"type":"text","text":"하지만 일시적으로 view hierarchy에서 사라지는 경우라면 strong으로 참조를 유지해주는 것이 좋다."}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"text":"사라졌다가 다시 보여져야하기 때문이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"하지만 보통은… 이런 경우에는 removeFromSuperView()를 호출하는 것보다 alpha를 조정하여 구현하는 것이 일반적이다.","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"성능적으로 차이가 있어서 이 방법을 더 선호하는 것으로 알려져있는데… 자세히 모르겠네. 나중에 더 자세히 파보는 걸로…🙄","type":"text"}],"type":"paragraph"}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"heading","anchor":"","level":1,"text":""},{"type":"paragraph","inlineContent":[{"text":"내가 경험했던 메모리 누수는 아래와 같은 상황이였다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"파라미터로 부모뷰를 전달 받아 addSubview를 호출하는 메소드가 구현되어있는 커스텀 뷰."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"전달받은 부모뷰를 커스텀 뷰 내부 "},{"type":"codeVoice","code":"parentView"},{"text":"라는 프로퍼티에 할당하는 작업이 존재.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"예를 들어 parentView에 ViewController의 view가 전달된다면, addSubview로 1번, parentView로 1번 총 참조카운트가 2번 발생되는 것이다.","type":"text"}]},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"이때 parentView 프로퍼티에 할당할 때 해당 프로퍼티는 강한 참조로 선언되어 있다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 view가 화면에서 사라져도 참조 카운트는 남아있어서 메모리 누수가 발생했다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"ViewController가 deinit되면서 view도 deinit 되어야하는데, customView에서 parentView로 강한 참조를 하고 있어서 deinit이 되지 않음."}]}]}]}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"text":"그래서 해당 문제는 parentView라는 프로퍼티를 weak로 선언하여 참조 카운팅이 되지 않도록 하여 문제를 해결하게 되었다.","type":"text"}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"참고링크"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uiview\/1622616-addsubview","type":"text"}]}]},{"content":[{"inlineContent":[{"text":"https:\/\/developer.apple.com\/forums\/thread\/15082","type":"text"}],"type":"paragraph"}]}]}]}]}],"kind":"content"}],"metadata":{"title":"230710 addSubview, Reference Count","roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}]},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/230710_addsubview_referencecount"]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/230710_addSubview_ReferenceCount","interfaceLanguage":"swift"},"references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://github.com/leeari95/TIL/assets/75905803/4602d6e6-7981-47e7-88d1-11a4a46f73ff":{"alt":"image","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/4602d6e6-7981-47e7-88d1-11a4a46f73ff"}],"identifier":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/4602d6e6-7981-47e7-88d1-11a4a46f73ff","type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/230904_keyboardlayoutguide.json b/docs/data/documentation/arinote/230904_keyboardlayoutguide.json new file mode 100644 index 0000000..9d0d25a --- /dev/null +++ b/docs/data/documentation/arinote/230904_keyboardlayoutguide.json @@ -0,0 +1 @@ +{"metadata":{"title":"230904 keyboardLayoutGuide","modules":[{"name":"Ari's Notes"}],"role":"article","roleHeading":"Article"},"variants":[{"paths":["\/documentation\/arinote\/230904_keyboardlayoutguide"],"traits":[{"interfaceLanguage":"swift"}]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/230904_keyboardLayoutGuide","interfaceLanguage":"swift"},"kind":"article","schemaVersion":{"major":0,"minor":3,"patch":0},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"sections":[],"primaryContentSections":[{"content":[{"type":"heading","text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned","level":1},{"type":"paragraph","inlineContent":[{"text":"9월 4일 (월)","type":"text"}]},{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading","text":"학습 내용","level":2},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"iOS 15부터 지원되는 keyboardLayoutGuide를 간단히 알아보자."}]}]}],"type":"unorderedList"},{"inlineContent":[{"type":"text","text":" "}],"type":"paragraph"},{"text":"고민한 점 \/ 해결 방법","type":"heading","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2},{"inlineContent":[{"type":"text","text":"기존 프로젝트에서 키보드의 높이에 따라 레이아웃을 업데이트하는 로직을 "},{"type":"codeVoice","code":"keyboardLayoutGuide"},{"type":"text","text":"을 활용하여 개선하는 작업을 진행하였다."}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"생각보다 많은 코드가 제거되었고, 간편하게 뷰를 구성할 수 있었다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"type":"heading","text":"사용 방법","level":3,"anchor":"%EC%82%AC%EC%9A%A9-%EB%B0%A9%EB%B2%95"},{"code":["\/\/ UIKit Constraint setup example","view.keyboardLayoutGuide.topAnchor.constraint(equalTo: textView.bottomAnchor).isActive = true","","\/\/ SnapKit Constarint setup example","view.keyboardLayoutGuide.snp.makeConstraints { make in"," make.top.eqaulTo(textView.snp.bottom)","}","","textView.snp.makeConstraints { make in "," make.bottom.eqaulTo(view.keyboardLayoutGuide.snp.top)","}"],"type":"codeListing","syntax":"swift"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"keyboardLayoutGuide는 키보드가 화면에 표시되고 있지 않으면 뷰의 하단 SafeArea와 일치한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"키보드가 화면에 표시되면 키보드를 기준으로 레이아웃이 따라간다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"터치 포인트가 겹치면 키보드 dismiss 제스처를 트래킹한다."}]}]}]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"level":3,"type":"heading","text":"간단 요약","anchor":"%EA%B0%84%EB%8B%A8-%EC%9A%94%EC%95%BD"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기존에 Notification을 통해서 키보드의 높이를 가져와서 계산하던 로직은 더이상 필요 없어지게 되었다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"키보드가 올라왔을 때 가려지지 말아야하는 뷰가 있다면 keyboardLayoutGuide를 기준으로 레이아웃을 설정해주면 된다!"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"더 자세한 활용법은 공식문서의 샘플 프로젝트를 참고해보자!","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"Reference","type":"heading","text":"Reference","level":1},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/uiview\/3752221-keyboardlayoutguide"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/keyboards_and_input\/adjusting_your_layout_with_keyboard_layout_guide"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/videos\/play\/wwdc2023\/10281\/","type":"text"}]}]}]}],"kind":"content"}],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/230911_opennotificationsettingsurlstring.json b/docs/data/documentation/arinote/230911_opennotificationsettingsurlstring.json new file mode 100644 index 0000000..e0eadbb --- /dev/null +++ b/docs/data/documentation/arinote/230911_opennotificationsettingsurlstring.json @@ -0,0 +1 @@ +{"sections":[],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/230911_openNotificationSettingsURLString"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"kind":"article","variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/230911_opennotificationsettingsurlstring"]}],"schemaVersion":{"major":0,"minor":3,"patch":0},"metadata":{"roleHeading":"Article","role":"article","modules":[{"name":"Ari's Notes"}],"title":"230911 openNotificationSettingsURLString"},"primaryContentSections":[{"content":[{"level":1,"text":"TIL (Today I Learned)","anchor":"TIL-Today-I-Learned","type":"heading"},{"inlineContent":[{"type":"text","text":"9월 9일 (토)"}],"type":"paragraph"},{"type":"heading","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"앱에서 설정으로 바로가는 동작은 있는데, 설정 > 알림으로 바로갈 수 있는 방법도 있을까?","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","type":"heading","level":2,"text":"고민한 점 \/ 해결 방법"},{"inlineContent":[{"text":"앱에서 설정 앱 > 앱의 알림 설정 화면으로 바로 이동하는 기능이 존재하기는 하는데…","type":"text"},{"type":"text","text":" "},{"type":"text","text":"iOS 16.0 부터만 가능한 것 같다."}],"type":"paragraph"},{"syntax":"swift","type":"codeListing","code":["@available(iOS 16.0, *)","static func notificationSettings() {"," guard let settingsURL = URL(string: UIApplication.openNotificationSettingsURLString) else {"," return"," }"," Task {"," UIApplication.shared.open(settingsURL)"," }","}"]},{"type":"thematicBreak"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/stackoverflow.com\/questions\/63334516\/ios-opening-app-push-notification-settings-screen-from-app"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/uikit\/uiapplication\/4013180-opennotificationsettingsurlstrin"}]}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/230915_uiimage_withtintcolor_renderingmode.json b/docs/data/documentation/arinote/230915_uiimage_withtintcolor_renderingmode.json new file mode 100644 index 0000000..9dfd568 --- /dev/null +++ b/docs/data/documentation/arinote/230915_uiimage_withtintcolor_renderingmode.json @@ -0,0 +1 @@ +{"schemaVersion":{"minor":3,"major":0,"patch":0},"variants":[{"paths":["\/documentation\/arinote\/230915_uiimage_withtintcolor_renderingmode"],"traits":[{"interfaceLanguage":"swift"}]}],"primaryContentSections":[{"content":[{"anchor":"TIL-Today-I-Learned","text":"TIL (Today I Learned)","type":"heading","level":1},{"inlineContent":[{"type":"text","text":"9월 15일 (금)"}],"type":"paragraph"},{"level":2,"type":"heading","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","text":"학습 내용"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UIImage에 tintColor를 적용했는데 원하는 색상이 제대로 입혀지지 않아서 알아보았다."}]}]}]},{"type":"paragraph","inlineContent":[{"text":" ","type":"text"}]},{"text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","type":"heading","level":2},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"원래 내가 tintColor를 적용하려고 했던 이미지의 원본 색상은 회색이였다."}]}]},{"content":[{"inlineContent":[{"text":"해당 이미지에 더 밝은 색상의 회색을 적용하려고 했지만, 원하는 밝기의 회색 색상이 적용되지 않았다.","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"UIImage의 withTintColor의 동작 방식"}],"type":"paragraph"},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"UIImage에 색상을 입히려면 renderingMode의 ","type":"text"},{"type":"codeVoice","code":"alwaysTemplate"},{"text":" 모드를 사용해야한다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"해당 모드는 "},{"type":"codeVoice","code":"이미지의 원래 색상 정보는 무시되고 알파값만 사용"},{"type":"text","text":"하게 된다."}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"즉, 이미지의 색상 대신 ","type":"text"},{"type":"codeVoice","code":"투명도만이 중요"},{"type":"text","text":"해진다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"text":"즉, 원본 이미지의 색상 정보는 무시하고 지정된 틴트 색상으로 이미지를 렌더링하는 효과를 얻게 된다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"지정된 틴트 색상은 원본 이미지의 투명도에 따라 적용된다."}]}]}],"type":"unorderedList"}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"따라서 제대로 적용되지 않았던 이유는, 원본 이미지의 알파값이 100% 가 아니였기 때문에 내가 원했던 색상이 적용되지 않았던 것이다.","type":"text"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"그래서 이미지의 원본 색상을 회색에서 흰색으로 수정해주고 withTintColor를 적용해주었더니 원하는 색상 값이 적용되었다.","type":"text"}]}]}]},{"type":"thematicBreak"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"참고링크"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uiimage\/3327300-withtintcolor","type":"text"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/uikit\/uiimage\/renderingmode","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/www.zehye.kr\/ios\/2021\/06\/12\/iOS_tintColor_image_rendering\/"}]}]}]}]}],"type":"unorderedList"}],"kind":"content"}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"sections":[],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/230915_UIImage_withTintColor_renderingMode","interfaceLanguage":"swift"},"kind":"article","metadata":{"role":"article","title":"230915 UIImage, withTintColor, renderingMode","roleHeading":"Article","modules":[{"name":"Ari's Notes"}]},"references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/231201_avplayer,_waitingtoplayatspecifiedrate,_waitingtoplayatspecifiedrate.json b/docs/data/documentation/arinote/231201_avplayer,_waitingtoplayatspecifiedrate,_waitingtoplayatspecifiedrate.json new file mode 100644 index 0000000..9710812 --- /dev/null +++ b/docs/data/documentation/arinote/231201_avplayer,_waitingtoplayatspecifiedrate,_waitingtoplayatspecifiedrate.json @@ -0,0 +1 @@ +{"kind":"article","identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/231201_AVPlayer,_waitingToPlayAtSpecifiedRate,_waitingToPlayAtSpecifiedRate","interfaceLanguage":"swift"},"schemaVersion":{"minor":3,"patch":0,"major":0},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/231201_avplayer,_waitingtoplayatspecifiedrate,_waitingtoplayatspecifiedrate"]}],"abstract":[{"type":"text","text":"12월 1일 (금)"}],"sections":[],"metadata":{"role":"article","modules":[{"name":"Ari's Notes"}],"title":"231201 AVPlayer, waitingToPlayAtSpecifiedRate, evaluatingBufferingRate","roleHeading":"Article"},"primaryContentSections":[{"kind":"content","content":[{"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","level":2,"type":"heading","text":"학습 내용"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"AVPlayer를 담은 셀을 재사용할 때, AVPlayerItem을 할당하여 재생하고 있는데, play()는 제대로 호출하고 있으나 waitingToPlayAtSpecifiedRate에서 playing으로 넘어가지 않고 무한 로딩이 걸리는 문제 해결","type":"text"}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","text":"고민한 점 \/ 해결 방법","level":2,"type":"heading"},{"style":"note","name":"Note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"비디오 스트리밍을 재생하는 셀이 컬렉션뷰 리로드를 할 때, 재생하던걸 멈추고 셀을 모두 리셋한 다음 다시 데이터가 설정되면서 비디오 스트리밍을 재생해야하는데, 몇개의 셀들이 간헐적으로 재생되지 않는 문제가 발생하여 이를 해결해보았다."}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"처음엔 비디오를 자동 재생을 하는 로직이나 재생하는 시점 로직에 문제가 생겨 사이드 이펙트가 일어나는 줄 알았다."},{"type":"text","text":" "},{"text":"하지만 디버깅 해보니 로직에는 문제가 없었고 정상적으로 ","type":"text"},{"type":"codeVoice","code":"AVPlayer의"},{"type":"text","text":" "},{"code":"play()","type":"codeVoice"},{"type":"text","text":" 메소드는 제대로 호출되고 있었다."}]},{"type":"paragraph","inlineContent":[{"type":"text","text":"그래서 AVPlayer의 "},{"code":"timeControlStatus","type":"codeVoice"},{"type":"text","text":", "},{"type":"codeVoice","code":"reasonForWaitingToPlay를"},{"text":" 관찰해보았다.","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"code":"timeControlStatus가","type":"codeVoice"},{"text":" ","type":"text"},{"type":"codeVoice","code":"waitingToPlayAtSpecifiedRate"},{"text":"에서 ","type":"text"},{"type":"codeVoice","code":"playing"},{"text":"으로 바뀌어야하는데, 간헐적으로 바뀌지 않고 계속 ","type":"text"},{"type":"codeVoice","code":"waitingToPlayAtSpecifiedRate"},{"type":"text","text":" 상태인 채로 멈춰서 재생을 하지 않는 것 같았다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"이때 ","type":"text"},{"type":"codeVoice","code":"reasonForWaitingToPlay를"},{"text":" 추가로 관찰해보니 ","type":"text"},{"code":"evaluatingBufferingRate에서","type":"codeVoice"},{"type":"text","text":" 멈춰서 더이상 상태가 바뀌지 않았다."}]}]}]},{"type":"paragraph","inlineContent":[{"text":"그래서 구글링을 해보았는데… AVPlayer의 자체적인 버그인 것 같았다. 비슷한 현상을 겪는 stackoverflow 글들이 몇개가 존재했다.","type":"text"}]},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"ios avplayer indefinite waitingToPlayAtSpecifiedRate"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":"그래서 "},{"type":"codeVoice","code":"play()"},{"type":"text","text":" 메소드를 호출하기 전에 아래와 같이 로직을 수정하였다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"AVPlayerItem"},{"text":"을 다시 초기화한다.","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"codeVoice","code":"replaceCurrentItem(with:)"},{"type":"text","text":" 메소드를 재호출시켜준다."}],"type":"paragraph"}]}]},{"type":"paragraph","inlineContent":[{"text":"일단 문제되었던 무한로딩 현상은 사라졌지만, 간헐적인 현상이였던지라 이게 정확한 원인인지는 잘 파악이 되진 않는다.","type":"text"},{"type":"text","text":"\n"},{"text":"하지만 일단 오늘 테스트해보았을 때, 더이상 비디오가 재생되지 않는 현상은 사라져서 이걸로 마무리하기로 했다!","type":"text"}]},{"type":"thematicBreak"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"참고링크","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/stackoverflow.com\/questions\/36858988\/avplayer-does-not-fire-playbackbufferempty-but-does-not-play-either","type":"text"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/avfoundation\/avplayer\/timecontrolstatus\/waitingtoplayatspecifiedrate"}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/avfoundation\/avplayer\/waitingreason\/1643489-evaluatingbufferingrate"}],"type":"paragraph"}]}]}]}]}]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"references":{"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/240104_userdefaults,_xcode_find_and_replace_regular_expression.json b/docs/data/documentation/arinote/240104_userdefaults,_xcode_find_and_replace_regular_expression.json new file mode 100644 index 0000000..481a38e --- /dev/null +++ b/docs/data/documentation/arinote/240104_userdefaults,_xcode_find_and_replace_regular_expression.json @@ -0,0 +1 @@ +{"metadata":{"modules":[{"name":"Ari's Notes"}],"roleHeading":"Article","role":"article","title":"240104 UserDefaults, Xcode Find and Replace Regular Expression"},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/240104_UserDefaults,_Xcode_Find_and_Replace_Regular_Expression","interfaceLanguage":"swift"},"primaryContentSections":[{"content":[{"anchor":"TIL-Today-I-Learned","level":1,"type":"heading","text":"TIL (Today I Learned)"},{"inlineContent":[{"type":"text","text":"1월 4일 (목)"}],"type":"paragraph"},{"text":"학습 내용","level":2,"anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9","type":"heading"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UserDefaults"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Xcode Find and Replace","type":"text"}]}]}]},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"text":"고민한 점 \/ 해결 방법","anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","level":2,"type":"heading"},{"style":"note","name":"Note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"UserDefaults는 기본적으로 캐시를 지원하고 있다…!"}]}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/bf597777-7c3e-4192-9add-a30fbdc5ef81","type":"image"}]},{"level":1,"anchor":"","text":"","type":"heading"},{"content":[{"inlineContent":[{"type":"text","text":"Xcode에서 정규 표현식을 사용하여 프로젝트 전체적으로 특정 코드를 바꾸는 방법"}],"type":"paragraph"}],"style":"note","type":"aside","name":"Note"},{"inlineContent":[{"type":"text","text":"회사 프로젝트 내에서 UserDefaults를 래핑하여 싱글톤 클래스로 get, set 메소드를 사용하고 있었는데, 이번에 SwiftyUserDefaults라는 라이브러리를 통해 개선하게 되면서 더이상 get, set 메소드를 만들어 래핑할 필요가 없어지게 되었다."}],"type":"paragraph"},{"type":"paragraph","inlineContent":[{"type":"text","text":"이 라이브러리는 subscript를 활용하여 데이터를 손쉽게 읽고 저장할 수 있는데, 프로젝트 코드를 전체적으로 메소드 호출 방식이 아닌, 참조 및 할당해주는 방식으로 바꿔주어야 한다."}]},{"inlineContent":[{"type":"text","text":"그래서 이를 일일이 바꿔주기에는 뭔가 노가다 같아서… 좋은 방법이 없을까 찾다가 발견했다."}],"type":"paragraph"},{"inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/923d8e74-eeca-40c9-922f-2cf3eb1a537f"}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"이렇게 텍스트 뿐만 아니라 정규 표현식을 사용하여 프로젝트 내에 특정 문자열을 골라낼 수가 있었다."},{"type":"text","text":" "},{"type":"text","text":"그리고 이를 활용하여 특정 문자열을 남겨두고 나머지 문자열만 바꿔치기가 가능했다."}],"type":"paragraph"},{"inlineContent":[{"type":"text","text":"예를 들어서 "},{"code":"Storage.shared.get(key: Persistent.store.keyName)","type":"codeVoice"},{"type":"text","text":" 이 부분을 "},{"type":"codeVoice","code":"Persistent.store.keyName"},{"type":"text","text":" 이런식으로 바꾸고 싶다고 가정해보자."}],"type":"paragraph"},{"type":"orderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"찾고자하는 패턴을 정규 표현식으로 아래와 같이 입력해준다."},{"text":" ","type":"text"},{"type":"text","text":"a. "},{"code":"Storage\\.shared\\.get\\(key: (.+)\\)","type":"codeVoice"},{"text":" ","type":"text"},{"text":"b. 여기서 ","type":"text"},{"type":"codeVoice","code":"(.+)"},{"type":"text","text":"는 괄호 안의 하나 이상의 임이의 문자를 캡처 그룹으로 지정한다는 의미다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"Replace 필드에 캡처그룹을 참조하는 $1를 입력해준다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"a. 이렇게 하면 찾은 문자열에서 캡쳐한 값으로 바꿔치기가 된다."}],"type":"paragraph"}]}]},{"inlineContent":[{"text":"아래는 또다른 예시다.","type":"text"}],"type":"paragraph"},{"type":"orderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"Find Pattern: "},{"code":"Storage\\.shared\\.set\\((.+), key: (.+)\\)","type":"codeVoice"}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"Replace Pattern: ","type":"text"},{"code":"$2 = $1","type":"codeVoice"}]}]}]},{"type":"thematicBreak"},{"text":"Reference","level":1,"anchor":"Reference","type":"heading"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/foundation\/userdefaults"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/xcode\/finding-and-replacing-content-in-a-project"}]}]}]}],"kind":"content"}],"schemaVersion":{"major":0,"minor":3,"patch":0},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/240104_userdefaults,_xcode_find_and_replace_regular_expression"]}],"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/Xcode"]]},"sections":[],"references":{"doc://AriNote/documentation/AriNote/Xcode":{"role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/Xcode","url":"\/documentation\/arinote\/xcode","title":"Xcode","kind":"article","abstract":[{"text":"언젠가…. 좋아질거지 Xcode…?","type":"text"}],"type":"topic"},"https://github.com/leeari95/TIL/assets/75905803/bf597777-7c3e-4192-9add-a30fbdc5ef81":{"alt":"image","variants":[{"url":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/bf597777-7c3e-4192-9add-a30fbdc5ef81","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/bf597777-7c3e-4192-9add-a30fbdc5ef81"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://github.com/leeari95/TIL/assets/75905803/923d8e74-eeca-40c9-922f-2cf3eb1a537f":{"alt":"image","variants":[{"url":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/923d8e74-eeca-40c9-922f-2cf3eb1a537f","traits":["1x","light"]}],"type":"image","identifier":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/923d8e74-eeca-40c9-922f-2cf3eb1a537f"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/240227_photokit,_phpickerviewcontroller,_phpickerconfiguration,_phassetcollection.json b/docs/data/documentation/arinote/240227_photokit,_phpickerviewcontroller,_phpickerconfiguration,_phassetcollection.json new file mode 100644 index 0000000..64ceb8a --- /dev/null +++ b/docs/data/documentation/arinote/240227_photokit,_phpickerviewcontroller,_phpickerconfiguration,_phassetcollection.json @@ -0,0 +1 @@ +{"schemaVersion":{"minor":3,"major":0,"patch":0},"primaryContentSections":[{"content":[{"text":"TIL (Today I Learned)","type":"heading","level":1,"anchor":"TIL-Today-I-Learned"},{"inlineContent":[{"text":"2월 27일 (화)","type":"text"}],"type":"paragraph"},{"type":"heading","level":2,"text":"학습 내용","anchor":"%ED%95%99%EC%8A%B5-%EB%82%B4%EC%9A%A9"},{"items":[{"content":[{"inlineContent":[{"text":"사용자 앨범에서 사진만 간단히 가져오는 방법","type":"text"}],"type":"paragraph"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"PhotoKit의 PHPickerViewController를 사용하여 사용자 앨범 불러오기"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자 앨범에서 사진 가져오기"}]}]}],"type":"unorderedList"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"text","text":" "}]},{"anchor":"%EA%B3%A0%EB%AF%BC%ED%95%9C-%EC%A0%90--%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95","type":"heading","level":2,"text":"고민한 점 \/ 해결 방법"},{"level":3,"anchor":"PhotoKit","text":"PhotoKit","type":"heading"},{"type":"paragraph","inlineContent":[{"type":"text","text":"PhotoKit은 사용자의 기기와 iCloud에 있는 사진에 대한 권한을 제공한다."}]},{"type":"paragraph","inlineContent":[{"type":"image","identifier":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/3a13b3b5-ca70-45cf-8ad8-e8daaaac5d12"}]},{"text":"","type":"heading","anchor":"","level":1},{"anchor":"PHPickerViewController","level":3,"text":"PHPickerViewController","type":"heading"},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/5e028574-faee-4b39-9fab-27b662efc32f","type":"image"}]},{"inlineContent":[{"type":"text","text":"iOS 14에서 새롭게 추가된 기능이다."}],"type":"paragraph"},{"inlineContent":[{"text":"특징은…","type":"text"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"기존에 느린 이미지 로딩과 복구 UI를 개선하였다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"라이브러리 사용 권한 요청 없이 이미지를 가져올 수 있다."}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"왜냐하면 PHPicker는 Limited Photo library access를 사용하기 때문이다. 즉, 사용자가 접근하도록 선택한 Asset에만 접근 가능하다는 이야기다."}]}]},{"content":[{"inlineContent":[{"type":"text","text":"사용자가 접근하도록 선택한 Asset은 사용자가 직접 선택한 이미지나 비디오 파일에만 접근 가능하다는 것으로 정리할 수 있다."}],"type":"paragraph"}]},{"content":[{"inlineContent":[{"text":"이 과정에서 중요한 점은 PHPickerViewController가 앱에 사용자의 전체 사진 라이브러리에 대한 접근 권한을 주지 않는다는 것이다.","type":"text"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"특정 항목들만 앱이 사용할 수 있게 되기 때문에 이 방식은 사용자의 개인 정보 보호를 강화하며 앱이 필요로 하는 최소한의 데이터에만 접근하도록 한다."}]}]}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"사용자가 동시에 여러 미디어 항목을 선택할 수 있다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"text":"사용자가 허락한 미디어만 사용 가능 및 이미지를 안정적으로 처리한다","type":"text"}]}]}]},{"text":"","level":1,"anchor":"","type":"heading"},{"anchor":"PHPickerViewController%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EC%97%AC-%EA%B0%84%EB%8B%A8%ED%95%98%EA%B2%8C-%EC%82%AC%EC%9A%A9%EC%9E%90-%EC%82%AC%EC%A7%84%EC%9D%84-%EA%B0%80%EC%A0%B8%EC%98%A4%EB%8A%94-%EB%B0%A9%EB%B2%95","type":"heading","level":3,"text":"PHPickerViewController를 사용하여 간단하게 사용자 사진을 가져오는 방법"},{"name":"Note","style":"note","content":[{"inlineContent":[{"text":"PHPickerConfiguration을 생성하여 각 속성들을 설정해준다.","type":"text"}],"type":"paragraph"}],"type":"aside"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/photokit\/phpickerconfiguration","type":"text"}],"type":"paragraph"}]}]},{"syntax":"swift","code":["var configuration = PHPickerConfiguration()","configuration.selectionLimit = 1 \/\/ 사진 선택 가능 갯수","configuration.selection = .default \/\/ 사진 선택 스타일 ","configuration.preferredAssetRepresentationMode = .current \/\/ 어떤 형식으로 받을지 결정하는 설정. 가장 최근 상태로 받아온다. 예를 들어, 사용자가 사진을 편집했다면 편집된 최신 버전을 받게 된다.","configuration.filter = .images \/\/ 사진만 필터링하여 가져오도록 하는 설정."],"type":"codeListing"},{"anchor":"","level":1,"text":"","type":"heading"},{"type":"aside","style":"note","name":"Note","content":[{"type":"paragraph","inlineContent":[{"text":"이후 PHPickerViewController를 생성하여 configuration을 할당해준다.","type":"text"}]}]},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/photokit\/phpickerviewcontroller","type":"text"}]}]}]},{"syntax":"swift","code":["let pickerViewController = PHPickerViewController(configuration: configuration)"],"type":"codeListing"},{"text":"","anchor":"","level":1,"type":"heading"},{"name":"Note","type":"aside","content":[{"type":"paragraph","inlineContent":[{"text":"해당 이미지 피커는 delegate를 할당할 수 있다.","type":"text"}]}],"style":"note"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"text":"https:\/\/developer.apple.com\/documentation\/photokit\/phpickerviewcontroller\/3606606-delegate","type":"text"}],"type":"paragraph"}]}]},{"syntax":"swift","type":"codeListing","code":["pickerViewController.delegate = self","rootViewController.present(pickerViewController, animated: true, completion: nil)"]},{"anchor":"","text":"","level":1,"type":"heading"},{"style":"note","type":"aside","content":[{"inlineContent":[{"text":"이 delegate를 통해 이미지 선택이 모두 완료되었을 때 시점에 동작을 커스텀할 수 있다.","type":"text"},{"text":" ","type":"text"},{"type":"text","text":"해당 delegate 메소드를 활용하여 results에 담겨져있는 데이터로 UIImage를 추출하여 사용하면 된다."}],"type":"paragraph"}],"name":"Note"},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/photokit\/phpickerviewcontrollerdelegate"}],"type":"paragraph"}]}]},{"syntax":"swift","type":"codeListing","code":["","\/\/ PHPickerViewControllerDelegate 메소드.","func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {"," \/\/ 사진을 한 장만 선택했을 때 간단한 예시."," let itemProviders = results.compactMap { $0.itemProvider }"," guard let itemProvider = itemProviders.first else { return }"," "," if itemProvider.canLoadObject(ofClass: UIImage.self) {"," itemProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in"," "," guard let self = self,"," let image = image as? UIImage else { return }"," "," \/\/ UI 업데이트를 위해 main thread로 변경"," DispatchQueue.main.async {"," self.imageView.image = image"," }"," }"," }","}"]},{"anchor":"","text":"","type":"heading","level":1},{"type":"heading","anchor":"%EC%82%AC%EC%9A%A9%EC%9E%90-%EB%94%94%EB%B0%94%EC%9D%B4%EC%8A%A4%EC%97%90-%EC%9E%88%EB%8A%94-%EC%95%A8%EB%B2%94-%EB%AA%A9%EB%A1%9D%EC%9D%84-%EA%B0%80%EC%A0%B8%EC%98%A4%EB%A0%A4%EB%A9%B4","text":"사용자 디바이스에 있는 앨범 목록을 가져오려면…?","level":3},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/photokit\/phassetcollection"}],"type":"paragraph"}]}],"type":"unorderedList"},{"type":"paragraph","inlineContent":[{"type":"codeVoice","code":"PHAssetCollection.fetchAssetCollections(with:subtype:options:)"},{"type":"text","text":" 메소드를 통해 앨범들을 가져올 수 있다."},{"type":"text","text":" "},{"text":"이 때는 사용자에게 ","type":"text"},{"identifier":"https:\/\/developer.apple.com\/documentation\/photokit\/phphotolibrary\/3616053-requestauthorization","type":"reference","isActive":true},{"type":"text","text":"을 하여 권한을 가져와야한다."},{"text":" ","type":"text"},{"text":"보통은 사용자 앨범을 가져와서 아래처럼 커스텀하게 앨범 목록을 표시해주려고 할 때 사용하는 듯 하다.","type":"text"}]},{"type":"paragraph","inlineContent":[{"identifier":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/ea4ab887-e6db-4d73-b3f7-e3ad862adf9f","type":"image"}]},{"type":"heading","anchor":"","text":"","level":1},{"anchor":"album-vs-smertAlbum","type":"heading","text":"album vs smertAlbum","level":4},{"items":[{"content":[{"type":"paragraph","inlineContent":[{"text":"album","type":"text"}]},{"type":"unorderedList","items":[{"content":[{"inlineContent":[{"type":"text","text":"앨범은 사용자가 직접 만든 앨범 목록들을 가져오게 된다."}],"type":"paragraph"}]}]}]},{"content":[{"inlineContent":[{"type":"text","text":"smartAlbum"}],"type":"paragraph"},{"type":"unorderedList","items":[{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"iOS 시스템에 의해 자동으로 생성되고 관리되는 앨범을 의미한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"예를 들어, ‘최근 항목’, ‘즐겨찾는 항목’, ‘셀피’ 등과 같은 카테고리가 여기에 속한다."}]}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"스마트 앨범은 특정 규칙이나 기준에 따라 자동으로 사진을 분류한다."}]}]}]}]}],"type":"unorderedList"},{"anchor":"Reference","level":1,"text":"Reference","type":"heading"},{"items":[{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/photokit"}],"type":"paragraph"}]},{"content":[{"type":"paragraph","inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/photokit\/phpickerviewcontroller"}]}]},{"content":[{"inlineContent":[{"type":"text","text":"https:\/\/developer.apple.com\/documentation\/photokit\/delivering_an_enhanced_privacy_experience_in_your_photos_app"}],"type":"paragraph"}]}],"type":"unorderedList"}],"kind":"content"}],"metadata":{"role":"article","roleHeading":"Article","title":"240227 PhotoKit, PHPickerViewController, PHPickerConfiguration, PHAssetCollection","modules":[{"name":"Ari's Notes"}]},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/240227_PhotoKit,_PHPickerViewController,_PHPickerConfiguration,_PHAssetCollection","interfaceLanguage":"swift"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/240227_photokit,_phpickerviewcontroller,_phpickerconfiguration,_phassetcollection"]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote","doc:\/\/AriNote\/documentation\/AriNote\/UIKit"]]},"kind":"article","sections":[],"references":{"https://github.com/leeari95/TIL/assets/75905803/ea4ab887-e6db-4d73-b3f7-e3ad862adf9f":{"alt":"image","identifier":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/ea4ab887-e6db-4d73-b3f7-e3ad862adf9f","variants":[{"url":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/ea4ab887-e6db-4d73-b3f7-e3ad862adf9f","traits":["1x","light"]}],"type":"image"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"https://github.com/leeari95/TIL/assets/75905803/3a13b3b5-ca70-45cf-8ad8-e8daaaac5d12":{"alt":"image","identifier":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/3a13b3b5-ca70-45cf-8ad8-e8daaaac5d12","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/3a13b3b5-ca70-45cf-8ad8-e8daaaac5d12"}],"type":"image"},"https://developer.apple.com/documentation/photokit/phphotolibrary/3616053-requestauthorization":{"titleInlineContent":[{"text":"앨범 접근 권한 요청","type":"text"}],"title":"앨범 접근 권한 요청","identifier":"https:\/\/developer.apple.com\/documentation\/photokit\/phphotolibrary\/3616053-requestauthorization","url":"https:\/\/developer.apple.com\/documentation\/photokit\/phphotolibrary\/3616053-requestauthorization","type":"link"},"doc://AriNote/documentation/AriNote/UIKit":{"kind":"article","title":"UIKit","abstract":[{"type":"text","text":"한몸이 되보자…"}],"type":"topic","role":"collectionGroup","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","url":"\/documentation\/arinote\/uikit"},"https://github.com/leeari95/TIL/assets/75905803/5e028574-faee-4b39-9fab-27b662efc32f":{"alt":"image","identifier":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/5e028574-faee-4b39-9fab-27b662efc32f","variants":[{"traits":["1x","light"],"url":"https:\/\/github.com\/leeari95\/TIL\/assets\/75905803\/5e028574-faee-4b39-9fab-27b662efc32f"}],"type":"image"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/architecture.json b/docs/data/documentation/arinote/architecture.json new file mode 100644 index 0000000..f30d2e7 --- /dev/null +++ b/docs/data/documentation/arinote/architecture.json @@ -0,0 +1 @@ +{"schemaVersion":{"patch":0,"minor":3,"major":0},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/architecture"]}],"abstract":[{"type":"text","text":"공부 많이해서 상황에 따라 적절한 아키텍처를 선택해보자."}],"sections":[],"metadata":{"role":"collectionGroup","roleHeading":"Overview","modules":[{"name":"Ari's Notes"}],"title":"Architecture"},"kind":"article","hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote"]]},"topicSections":[{"anchor":"Topics","identifiers":["doc:\/\/AriNote\/documentation\/AriNote\/220324_-----_----","doc:\/\/AriNote\/documentation\/AriNote\/220322_Flyweight,_Facade","doc:\/\/AriNote\/documentation\/AriNote\/220303_Clean_Architecture_MVVM,_async,_await","doc:\/\/AriNote\/documentation\/AriNote\/220301_MVVM,_----_--"]}],"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/Architecture","interfaceLanguage":"swift"},"references":{"doc://AriNote/documentation/AriNote/220322_Flyweight,_Facade":{"type":"topic","abstract":[],"kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220322_Flyweight,_Facade","title":"220322 Flyweight, Facade","url":"\/documentation\/arinote\/220322_flyweight,_facade","role":"article"},"doc://AriNote/documentation/AriNote/220324_-----_----":{"url":"\/documentation\/arinote\/220324_-----_----","title":"220324 디자인패턴 활동학습","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220324_-----_----","role":"article","type":"topic","kind":"article","abstract":[]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/220301_MVVM,_----_--":{"role":"article","abstract":[],"kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220301_MVVM,_----_--","title":"220301 MVVM, 프로젝트 설계","url":"\/documentation\/arinote\/220301_mvvm,_----_--","type":"topic"},"doc://AriNote/documentation/AriNote/220303_Clean_Architecture_MVVM,_async,_await":{"url":"\/documentation\/arinote\/220303_clean_architecture_mvvm,_async,_await","title":"220303 Clean Architecture MVVM, async, await","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220303_Clean_Architecture_MVVM,_async,_await","role":"article","type":"topic","kind":"article","abstract":[]}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/cs.json b/docs/data/documentation/arinote/cs.json new file mode 100644 index 0000000..c47cda1 --- /dev/null +++ b/docs/data/documentation/arinote/cs.json @@ -0,0 +1 @@ +{"kind":"article","variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/cs"]}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote"]]},"schemaVersion":{"minor":3,"major":0,"patch":0},"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/CS"},"topicSections":[{"identifiers":["doc:\/\/AriNote\/documentation\/AriNote\/220523_API,_REST_API,_Array,_Set,_Tuple,_Library,_map","doc:\/\/AriNote\/documentation\/AriNote\/220310_OAuth,_Social_Login,_TLS,_---,_----,_---,_---","doc:\/\/AriNote\/documentation\/AriNote\/220225_Greedy,_wholeNumberValue,_enumerated,_reduce","doc:\/\/AriNote\/documentation\/AriNote\/220113_Cache,_pagination","doc:\/\/AriNote\/documentation\/AriNote\/220110_NSCache","doc:\/\/AriNote\/documentation\/AriNote\/220104_multipart,_form-data,_aync_method_test,_---_--,_Test_Doubles","doc:\/\/AriNote\/documentation\/AriNote\/211230_HTTP,_UDID,_UUID","doc:\/\/AriNote\/documentation\/AriNote\/211229_TCP_IP","doc:\/\/AriNote\/documentation\/AriNote\/211228_Race_Condition,_DispatchSemaphore,_HTTP","doc:\/\/AriNote\/documentation\/AriNote\/211223_POP,_delegate","doc:\/\/AriNote\/documentation\/AriNote\/211217_GCD","doc:\/\/AriNote\/documentation\/AriNote\/211216_sync,_async,_Parallel,_Concurrency,_GCD","doc:\/\/AriNote\/documentation\/AriNote\/211215_-----_------","doc:\/\/AriNote\/documentation\/AriNote\/211207_NSDataAsset,_----,_Contents.JSON,_Framework,_Library,_API","doc:\/\/AriNote\/documentation\/AriNote\/211018_let,_var,_tool_bar,_Design_Patterns,_Architectures,_MVC,_Dictionary,_Downcasting,_Function_Types_as_Parameter_Types","doc:\/\/AriNote\/documentation\/AriNote\/210812_---_--","doc:\/\/AriNote\/documentation\/AriNote\/210804_-------","doc:\/\/AriNote\/documentation\/AriNote\/210802_---_--"],"anchor":"Topics"}],"metadata":{"modules":[{"name":"Ari's Notes"}],"role":"collectionGroup","title":"CS","roleHeading":"Overview"},"sections":[],"abstract":[{"text":"Computer Science","type":"text"}],"references":{"doc://AriNote/documentation/AriNote/211229_TCP_IP":{"title":"211229 TCP\/IP","role":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211229_TCP_IP","url":"\/documentation\/arinote\/211229_tcp_ip","kind":"article","abstract":[],"type":"topic"},"doc://AriNote/documentation/AriNote/220310_OAuth,_Social_Login,_TLS,_---,_----,_---,_---":{"abstract":[],"type":"topic","kind":"article","role":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220310_OAuth,_Social_Login,_TLS,_---,_----,_---,_---","title":"220310 OAuth, Social Login, TLS, 대칭키, 비대칭키, 암호화, 복호화","url":"\/documentation\/arinote\/220310_oauth,_social_login,_tls,_---,_----,_---,_---"},"doc://AriNote/documentation/AriNote/211223_POP,_delegate":{"title":"211223 POP, delegate","role":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211223_POP,_delegate","url":"\/documentation\/arinote\/211223_pop,_delegate","kind":"article","abstract":[],"type":"topic"},"doc://AriNote/documentation/AriNote/220104_multipart,_form-data,_aync_method_test,_---_--,_Test_Doubles":{"type":"topic","kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220104_multipart,_form-data,_aync_method_test,_---_--,_Test_Doubles","role":"article","title":"220104 multipart, form-data, aync method test, 의존성 주입, Test Doubles","url":"\/documentation\/arinote\/220104_multipart,_form-data,_aync_method_test,_---_--,_test_doubles","abstract":[]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/220113_Cache,_pagination":{"type":"topic","title":"220113 Cache, pagination","role":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220113_Cache,_pagination","url":"\/documentation\/arinote\/220113_cache,_pagination","kind":"article","abstract":[]},"doc://AriNote/documentation/AriNote/211207_NSDataAsset,_----,_Contents.JSON,_Framework,_Library,_API":{"kind":"article","type":"topic","title":"211207 NSDataAsset, 오답노트, Contents.JSON, Framework, Library, API","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211207_NSDataAsset,_----,_Contents.JSON,_Framework,_Library,_API","url":"\/documentation\/arinote\/211207_nsdataasset,_----,_contents.json,_framework,_library,_api","abstract":[],"role":"article"},"doc://AriNote/documentation/AriNote/210802_---_--":{"type":"topic","title":"210802 정수와 실수","role":"article","kind":"article","url":"\/documentation\/arinote\/210802_---_--","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/210802_---_--","abstract":[]},"doc://AriNote/documentation/AriNote/211230_HTTP,_UDID,_UUID":{"kind":"article","type":"topic","title":"211230 HTTP, UDID, UUID","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211230_HTTP,_UDID,_UUID","url":"\/documentation\/arinote\/211230_http,_udid,_uuid","abstract":[],"role":"article"},"doc://AriNote/documentation/AriNote/211217_GCD":{"url":"\/documentation\/arinote\/211217_gcd","type":"topic","kind":"article","title":"211217 GCD","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211217_GCD","abstract":[],"role":"article"},"doc://AriNote/documentation/AriNote/211216_sync,_async,_Parallel,_Concurrency,_GCD":{"url":"\/documentation\/arinote\/211216_sync,_async,_parallel,_concurrency,_gcd","type":"topic","kind":"article","title":"211216 sync, async, Parallel, Concurrency, GCD","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211216_sync,_async,_Parallel,_Concurrency,_GCD","abstract":[],"role":"article"},"doc://AriNote/documentation/AriNote/211215_-----_------":{"abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211215_-----_------","type":"topic","role":"article","url":"\/documentation\/arinote\/211215_-----_------","kind":"article","title":"211215 운영체제와 데이터베이스.md"},"doc://AriNote/documentation/AriNote/220110_NSCache":{"url":"\/documentation\/arinote\/220110_nscache","type":"topic","kind":"article","title":"220110 NSCache","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220110_NSCache","abstract":[],"role":"article"},"doc://AriNote/documentation/AriNote/210812_---_--":{"url":"\/documentation\/arinote\/210812_---_--","abstract":[],"title":"210812 메모리 구조","role":"article","kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/210812_---_--","type":"topic"},"doc://AriNote/documentation/AriNote/220225_Greedy,_wholeNumberValue,_enumerated,_reduce":{"url":"\/documentation\/arinote\/220225_greedy,_wholenumbervalue,_enumerated,_reduce","type":"topic","kind":"article","title":"220225 Greedy, wholeNumberValue, enumerated, reduce","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220225_Greedy,_wholeNumberValue,_enumerated,_reduce","role":"article","abstract":[]},"doc://AriNote/documentation/AriNote/210804_-------":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/210804_-------","title":"210804 동적프로그래밍","type":"topic","kind":"article","url":"\/documentation\/arinote\/210804_-------","abstract":[],"role":"article"},"doc://AriNote/documentation/AriNote/211018_let,_var,_tool_bar,_Design_Patterns,_Architectures,_MVC,_Dictionary,_Downcasting,_Function_Types_as_Parameter_Types":{"url":"\/documentation\/arinote\/211018_let,_var,_tool_bar,_design_patterns,_architectures,_mvc,_dictionary,_downcasting,_function_types_as_parameter_types","kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211018_let,_var,_tool_bar,_Design_Patterns,_Architectures,_MVC,_Dictionary,_Downcasting,_Function_Types_as_Parameter_Types","abstract":[],"title":"211018 let, var, tool bar, Design Patterns, Architectures, MVC, Dictionary, Downcasting, Function Types as Parameter Types","role":"article","type":"topic"},"doc://AriNote/documentation/AriNote/220523_API,_REST_API,_Array,_Set,_Tuple,_Library,_map":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220523_API,_REST_API,_Array,_Set,_Tuple,_Library,_map","url":"\/documentation\/arinote\/220523_api,_rest_api,_array,_set,_tuple,_library,_map","kind":"article","abstract":[],"type":"topic","title":"220523 API, REST API, Array, Set, Tuple, Library, map","role":"article"},"doc://AriNote/documentation/AriNote/211228_Race_Condition,_DispatchSemaphore,_HTTP":{"kind":"article","url":"\/documentation\/arinote\/211228_race_condition,_dispatchsemaphore,_http","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211228_Race_Condition,_DispatchSemaphore,_HTTP","abstract":[],"title":"211228 Race Condition, DispatchSemaphore, HTTP","role":"article","type":"topic"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/git.json b/docs/data/documentation/arinote/git.json new file mode 100644 index 0000000..f78d540 --- /dev/null +++ b/docs/data/documentation/arinote/git.json @@ -0,0 +1 @@ +{"kind":"article","sections":[],"variants":[{"paths":["\/documentation\/arinote\/git"],"traits":[{"interfaceLanguage":"swift"}]}],"abstract":[{"type":"text","text":"Git 관련 지식"}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote"]]},"topicSections":[{"anchor":"Topics","identifiers":["doc:\/\/AriNote\/documentation\/AriNote\/220613_Github_Actions,_CI,_CD,_---","doc:\/\/AriNote\/documentation\/AriNote\/211005_Git,-_-----_--","doc:\/\/AriNote\/documentation\/AriNote\/211004_---------,_--------------,_Git----"]}],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/Git"},"schemaVersion":{"patch":0,"major":0,"minor":3},"metadata":{"title":"Git","roleHeading":"Overview","role":"collectionGroup","modules":[{"name":"Ari's Notes"}]},"references":{"doc://AriNote/documentation/AriNote/220613_Github_Actions,_CI,_CD,_---":{"role":"article","abstract":[],"kind":"article","title":"220613 Github Actions, CI, CD, 자동화","url":"\/documentation\/arinote\/220613_github_actions,_ci,_cd,_---","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220613_Github_Actions,_CI,_CD,_---","type":"topic"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/211004_---------,_--------------,_Git----":{"type":"topic","kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211004_---------,_--------------,_Git----","role":"article","url":"\/documentation\/arinote\/211004_---------,_--------------,_git----","abstract":[],"title":"211004 사전과제, 짝프로그래밍, Git기초"},"doc://AriNote/documentation/AriNote/211005_Git,-_-----_--":{"role":"article","abstract":[],"kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211005_Git,-_-----_--","url":"\/documentation\/arinote\/211005_git,-_-----_--","title":"211004 사전과제, 짝프로그래밍, Git기초","type":"topic"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/rx.json b/docs/data/documentation/arinote/rx.json new file mode 100644 index 0000000..3dd14f4 --- /dev/null +++ b/docs/data/documentation/arinote/rx.json @@ -0,0 +1 @@ +{"kind":"article","topicSections":[{"identifiers":["doc:\/\/AriNote\/documentation\/AriNote\/220813_RxSwift,_Almofire,_URLProtocol,_Unit_Test","doc:\/\/AriNote\/documentation\/AriNote\/220314_MVVM,_Rx,_changed,_coordinator,_observable","doc:\/\/AriNote\/documentation\/AriNote\/220304_RxSwift,_Observable,_Subject","doc:\/\/AriNote\/documentation\/AriNote\/220218_RxSwift,_RxCocoa,_RxRelay,_MVVM","doc:\/\/AriNote\/documentation\/AriNote\/220217_Dropbox,_RxSwift,_SPM,_DispatchGroup"],"anchor":"Topics"}],"variants":[{"paths":["\/documentation\/arinote\/rx"],"traits":[{"interfaceLanguage":"swift"}]}],"schemaVersion":{"patch":0,"minor":3,"major":0},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote"]]},"abstract":[{"text":"RxSwift를 활용한 비동기 프로그래밍","type":"text"}],"sections":[],"identifier":{"interfaceLanguage":"swift","url":"doc:\/\/AriNote\/documentation\/AriNote\/Rx"},"metadata":{"roleHeading":"Overview","modules":[{"name":"Ari's Notes"}],"title":"RxSwift","role":"collectionGroup"},"references":{"doc://AriNote/documentation/AriNote/220813_RxSwift,_Almofire,_URLProtocol,_Unit_Test":{"kind":"article","role":"article","url":"\/documentation\/arinote\/220813_rxswift,_almofire,_urlprotocol,_unit_test","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220813_RxSwift,_Almofire,_URLProtocol,_Unit_Test","type":"topic","title":"220813 RxSwift, Almofire, URLProtocol, Unit Test","abstract":[]},"doc://AriNote/documentation/AriNote/220217_Dropbox,_RxSwift,_SPM,_DispatchGroup":{"kind":"article","url":"\/documentation\/arinote\/220217_dropbox,_rxswift,_spm,_dispatchgroup","role":"article","type":"topic","title":"220217 Dropbox, RxSwift, SPM, DispatchGroup","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220217_Dropbox,_RxSwift,_SPM,_DispatchGroup"},"doc://AriNote/documentation/AriNote/220218_RxSwift,_RxCocoa,_RxRelay,_MVVM":{"kind":"article","role":"article","url":"\/documentation\/arinote\/220218_rxswift,_rxcocoa,_rxrelay,_mvvm","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220218_RxSwift,_RxCocoa,_RxRelay,_MVVM","type":"topic","title":"220218 RxSwift, RxCocoa, RxRelay, MVVM","abstract":[]},"doc://AriNote/documentation/AriNote/220304_RxSwift,_Observable,_Subject":{"kind":"article","url":"\/documentation\/arinote\/220304_rxswift,_observable,_subject","role":"article","type":"topic","title":"220304 RxSwift, Observable, Subject","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220304_RxSwift,_Observable,_Subject"},"doc://AriNote/documentation/AriNote/220314_MVVM,_Rx,_changed,_coordinator,_observable":{"kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220314_MVVM,_Rx,_changed,_coordinator,_observable","role":"article","url":"\/documentation\/arinote\/220314_mvvm,_rx,_changed,_coordinator,_observable","abstract":[],"type":"topic","title":"220314 MVVM, Rx, changed, coordinator, observable"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/swift.json b/docs/data/documentation/arinote/swift.json new file mode 100644 index 0000000..90594cc --- /dev/null +++ b/docs/data/documentation/arinote/swift.json @@ -0,0 +1 @@ +{"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/Swift","interfaceLanguage":"swift"},"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/swift"]}],"topicSections":[{"identifiers":["doc:\/\/AriNote\/documentation\/AriNote\/220923_URLSession,_CachePolicy","doc:\/\/AriNote\/documentation\/AriNote\/220105_URLProtocolMock,_WWDC2018,_URLSession,_NetworkTest","doc:\/\/AriNote\/documentation\/AriNote\/211231_inout,_async,_await,_Thread_Sanitizer","doc:\/\/AriNote\/documentation\/AriNote\/211224_escapingClosure,_autoClosure,_weak,_wildcard,_naming","doc:\/\/AriNote\/documentation\/AriNote\/211121_OSLog","doc:\/\/AriNote\/documentation\/AriNote\/211112_Delegation,_NumberFormatter,_Finding_and_Refactoring_Code,_SnapKit","doc:\/\/AriNote\/documentation\/AriNote\/211111_Capture_List,_SOLID,_protocol,_split,_UML","doc:\/\/AriNote\/documentation\/AriNote\/211109_removeAll,_attribute,_commit,_Numeric,_FloatingPoint,_NaN,_where,_SourceTree","doc:\/\/AriNote\/documentation\/AriNote\/211108_Delegate,_weak,_ARC,_Element,_Queue,_Stack,_LinkeList","doc:\/\/AriNote\/documentation\/AriNote\/211020_KVC,_Notification,_NotificationCenter","doc:\/\/AriNote\/documentation\/AriNote\/211019_Nested_Type,_operator,_Dictionary,_Sequence,_LocalizedError,_Error_Handling","doc:\/\/AriNote\/documentation\/AriNote\/211013_Naming,_if-else,_switch,_----_----,_reset,_reflog","doc:\/\/AriNote\/documentation\/AriNote\/211012_----,_Properties,_KeyPath,_KVC,_iOS,_HIG","doc:\/\/AriNote\/documentation\/AriNote\/211011_condition,_condition-list,_flow_chart,_------------,_Ground_rules,_Daily_Scrum,_----","doc:\/\/AriNote\/documentation\/AriNote\/211009_Optional,_Dictionary,_compactMap","doc:\/\/AriNote\/documentation\/AriNote\/211008_Destructuring,_commit,_branch,_Changes_requested,_refactoring,_-----_----_--,_naming,_reduce","doc:\/\/AriNote\/documentation\/AriNote\/211007_----,---,KVO,compactMap,----","doc:\/\/AriNote\/documentation\/AriNote\/211006_------,lazy,---,----,----,-----_----_----","doc:\/\/AriNote\/documentation\/AriNote\/210831_Result","doc:\/\/AriNote\/documentation\/AriNote\/210827_FileManger","doc:\/\/AriNote\/documentation\/AriNote\/210820_------,_----_---","doc:\/\/AriNote\/documentation\/AriNote\/210817_----","doc:\/\/AriNote\/documentation\/AriNote\/210816_----","doc:\/\/AriNote\/documentation\/AriNote\/210811_Swift---_--_let---_----------","doc:\/\/AriNote\/documentation\/AriNote\/210809_enum","doc:\/\/AriNote\/documentation\/AriNote\/210805_API_Design_Guidelines","doc:\/\/AriNote\/documentation\/AriNote\/210803_for-in_-_forEach"],"anchor":"Topics"}],"sections":[],"kind":"article","abstract":[{"text":"언어 공부","type":"text"}],"schemaVersion":{"minor":3,"major":0,"patch":0},"metadata":{"role":"collectionGroup","modules":[{"name":"Ari's Notes"}],"roleHeading":"Overview","title":"Swift"},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote"]]},"references":{"doc://AriNote/documentation/AriNote/211006_------,lazy,---,----,----,-----_----_----":{"kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211006_------,lazy,---,----,----,-----_----_----","url":"\/documentation\/arinote\/211006_------,lazy,---,----,----,-----_----_----","role":"article","abstract":[],"title":"211006 첫팀프로젝트,lazy,가독성,성능고려,재사용성,고차함수와 반복문의 성능차이","type":"topic"},"doc://AriNote/documentation/AriNote/211013_Naming,_if-else,_switch,_----_----,_reset,_reflog":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211013_Naming,_if-else,_switch,_----_----,_reset,_reflog","kind":"article","role":"article","url":"\/documentation\/arinote\/211013_naming,_if-else,_switch,_----_----,_reset,_reflog","abstract":[],"title":"211013 Naming, if-else, switch, 반복문과 재귀함수, reset, reflog","type":"topic"},"doc://AriNote/documentation/AriNote/210811_Swift---_--_let---_----------":{"kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/210811_Swift---_--_let---_----------","url":"\/documentation\/arinote\/210811_swift---_--_let---_----------","role":"article","abstract":[],"title":"210811 Swift는 왜 let을 선호할까?","type":"topic"},"doc://AriNote/documentation/AriNote/211111_Capture_List,_SOLID,_protocol,_split,_UML":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211111_Capture_List,_SOLID,_protocol,_split,_UML","kind":"article","role":"article","url":"\/documentation\/arinote\/211111_capture_list,_solid,_protocol,_split,_uml","abstract":[],"title":"211111 Capture List, SOLID, protocol, split, UML","type":"topic"},"doc://AriNote/documentation/AriNote/211108_Delegate,_weak,_ARC,_Element,_Queue,_Stack,_LinkeList":{"kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211108_Delegate,_weak,_ARC,_Element,_Queue,_Stack,_LinkeList","url":"\/documentation\/arinote\/211108_delegate,_weak,_arc,_element,_queue,_stack,_linkelist","role":"article","abstract":[],"title":"211108 Delegate, weak, ARC, Element, Queue, Stack, LinkeList","type":"topic"},"doc://AriNote/documentation/AriNote/211012_----,_Properties,_KeyPath,_KVC,_iOS,_HIG":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211012_----,_Properties,_KeyPath,_KVC,_iOS,_HIG","kind":"article","role":"article","url":"\/documentation\/arinote\/211012_----,_properties,_keypath,_kvc,_ios,_hig","abstract":[],"title":"211012 프로젝트, Properties, KeyPath, KVC, iOS, HIG","type":"topic"},"doc://AriNote/documentation/AriNote/211011_condition,_condition-list,_flow_chart,_------------,_Ground_rules,_Daily_Scrum,_----":{"kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211011_condition,_condition-list,_flow_chart,_------------,_Ground_rules,_Daily_Scrum,_----","url":"\/documentation\/arinote\/211011_condition,_condition-list,_flow_chart,_------------,_ground_rules,_daily_scrum,_----","role":"article","abstract":[],"title":"211011 condition, condition-list, flow chart, 일반화추상화은닉화캡슐화, Ground rules, Daily Scrum, 메소드명","type":"topic"},"doc://AriNote/documentation/AriNote/211020_KVC,_Notification,_NotificationCenter":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211020_KVC,_Notification,_NotificationCenter","kind":"article","role":"article","url":"\/documentation\/arinote\/211020_kvc,_notification,_notificationcenter","abstract":[],"title":"211020 KVC, Notification, NotificationCenter","type":"topic"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/210817_----":{"kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/210817_----","url":"\/documentation\/arinote\/210817_----","role":"article","abstract":[],"title":"210817 프로토콜","type":"topic"},"doc://AriNote/documentation/AriNote/211008_Destructuring,_commit,_branch,_Changes_requested,_refactoring,_-----_----_--,_naming,_reduce":{"type":"topic","abstract":[],"role":"article","title":"211008 Destructuring, commit, branch, Changes requested, refactoring, 전역변수를 잘안쓰는 이유, naming, reduce","url":"\/documentation\/arinote\/211008_destructuring,_commit,_branch,_changes_requested,_refactoring,_-----_----_--,_naming,_reduce","kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211008_Destructuring,_commit,_branch,_Changes_requested,_refactoring,_-----_----_--,_naming,_reduce"},"doc://AriNote/documentation/AriNote/210831_Result":{"kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/210831_Result","role":"article","url":"\/documentation\/arinote\/210831_result","abstract":[],"title":"210831 Result","type":"topic"},"doc://AriNote/documentation/AriNote/210820_------,_----_---":{"title":"210820 이니셜라이저, 궁금했던 키워드","kind":"article","role":"article","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/210820_------,_----_---","url":"\/documentation\/arinote\/210820_------,_----_---","abstract":[]},"doc://AriNote/documentation/AriNote/220105_URLProtocolMock,_WWDC2018,_URLSession,_NetworkTest":{"abstract":[],"title":"220105 URLProtocolMock, WWDC2018, URLSession, NetworkTest","kind":"article","role":"article","url":"\/documentation\/arinote\/220105_urlprotocolmock,_wwdc2018,_urlsession,_networktest","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220105_URLProtocolMock,_WWDC2018,_URLSession,_NetworkTest"},"doc://AriNote/documentation/AriNote/211007_----,---,KVO,compactMap,----":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211007_----,---,KVO,compactMap,----","kind":"article","role":"article","url":"\/documentation\/arinote\/211007_----,---,kvo,compactmap,----","abstract":[],"title":"211007 땅따먹기,싱글톤,KVO,compactMap,프로젝트","type":"topic"},"doc://AriNote/documentation/AriNote/210805_API_Design_Guidelines":{"abstract":[],"title":"210805 API Design Guidelines","kind":"article","type":"topic","url":"\/documentation\/arinote\/210805_api_design_guidelines","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/210805_API_Design_Guidelines","role":"article"},"doc://AriNote/documentation/AriNote/210803_for-in_-_forEach":{"title":"210803 for-in 외 forEach","role":"article","url":"\/documentation\/arinote\/210803_for-in_-_foreach","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/210803_for-in_-_forEach","kind":"article","abstract":[]},"doc://AriNote/documentation/AriNote/210816_----":{"abstract":[],"url":"\/documentation\/arinote\/210816_----","type":"topic","role":"article","title":"210816 접근제어","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/210816_----","kind":"article"},"doc://AriNote/documentation/AriNote/210809_enum":{"kind":"article","title":"210809 enum","abstract":[],"type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/210809_enum","url":"\/documentation\/arinote\/210809_enum","role":"article"},"doc://AriNote/documentation/AriNote/211121_OSLog":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211121_OSLog","abstract":[],"kind":"article","title":"211121 OSLog","url":"\/documentation\/arinote\/211121_oslog","type":"topic","role":"article"},"doc://AriNote/documentation/AriNote/211112_Delegation,_NumberFormatter,_Finding_and_Refactoring_Code,_SnapKit":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211112_Delegation,_NumberFormatter,_Finding_and_Refactoring_Code,_SnapKit","type":"topic","title":"211112 Delegation, NumberFormatter, Finding and Refactoring Code, SnapKit","url":"\/documentation\/arinote\/211112_delegation,_numberformatter,_finding_and_refactoring_code,_snapkit","role":"article","abstract":[],"kind":"article"},"doc://AriNote/documentation/AriNote/210827_FileManger":{"url":"\/documentation\/arinote\/210827_filemanger","title":"210827 FileManger","kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/210827_FileManger","type":"topic","abstract":[],"role":"article"},"doc://AriNote/documentation/AriNote/211231_inout,_async,_await,_Thread_Sanitizer":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211231_inout,_async,_await,_Thread_Sanitizer","type":"topic","title":"211231 inout, async, await, Thread Sanitizer","url":"\/documentation\/arinote\/211231_inout,_async,_await,_thread_sanitizer","role":"article","abstract":[],"kind":"article"},"doc://AriNote/documentation/AriNote/211224_escapingClosure,_autoClosure,_weak,_wildcard,_naming":{"url":"\/documentation\/arinote\/211224_escapingclosure,_autoclosure,_weak,_wildcard,_naming","title":"211224 escapingClosure, autoClosure, weak, wildcard, naming","kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211224_escapingClosure,_autoClosure,_weak,_wildcard,_naming","type":"topic","abstract":[],"role":"article"},"doc://AriNote/documentation/AriNote/220923_URLSession,_CachePolicy":{"role":"article","type":"topic","kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220923_URLSession,_CachePolicy","title":"220923 URLSession, CachePolicy","abstract":[],"url":"\/documentation\/arinote\/220923_urlsession,_cachepolicy"},"doc://AriNote/documentation/AriNote/211019_Nested_Type,_operator,_Dictionary,_Sequence,_LocalizedError,_Error_Handling":{"url":"\/documentation\/arinote\/211019_nested_type,_operator,_dictionary,_sequence,_localizederror,_error_handling","title":"211019 Nested Type, operator, Dictionary, Sequence, LocalizedError, Error Handling","kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211019_Nested_Type,_operator,_Dictionary,_Sequence,_LocalizedError,_Error_Handling","type":"topic","abstract":[],"role":"article"},"doc://AriNote/documentation/AriNote/211009_Optional,_Dictionary,_compactMap":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211009_Optional,_Dictionary,_compactMap","type":"topic","title":"211009 Optional, Dictionary, compactMap","url":"\/documentation\/arinote\/211009_optional,_dictionary,_compactmap","role":"article","abstract":[],"kind":"article"},"doc://AriNote/documentation/AriNote/211109_removeAll,_attribute,_commit,_Numeric,_FloatingPoint,_NaN,_where,_SourceTree":{"url":"\/documentation\/arinote\/211109_removeall,_attribute,_commit,_numeric,_floatingpoint,_nan,_where,_sourcetree","title":"211109 removeAll, attribute, commit, Numeric, FloatingPoint, NaN, where, SourceTree","kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211109_removeAll,_attribute,_commit,_Numeric,_FloatingPoint,_NaN,_where,_SourceTree","type":"topic","abstract":[],"role":"article"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/swiftui.json b/docs/data/documentation/arinote/swiftui.json new file mode 100644 index 0000000..a2ce655 --- /dev/null +++ b/docs/data/documentation/arinote/swiftui.json @@ -0,0 +1 @@ +{"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/SwiftUI","interfaceLanguage":"swift"},"schemaVersion":{"patch":0,"minor":3,"major":0},"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote"]]},"primaryContentSections":[{"kind":"content","content":[{"text":"Overview","anchor":"Overview","type":"heading","level":2},{"type":"paragraph","inlineContent":[{"type":"text","text":"d"}]}]}],"variants":[{"traits":[{"interfaceLanguage":"swift"}],"paths":["\/documentation\/arinote\/swiftui"]}],"metadata":{"role":"collectionGroup","title":"SwiftUI","modules":[{"name":"Ari's Notes"}],"roleHeading":"Overview"},"kind":"article","sections":[],"abstract":[{"type":"text","text":"언능 UIKit만큼 SwiftUI를 더 잘 알고 싶다!"}],"topicSections":[{"identifiers":["doc:\/\/AriNote\/documentation\/AriNote\/220317_Widget,_Timeline,_Decorator_Pattern","doc:\/\/AriNote\/documentation\/AriNote\/220315_Memory_Leak,_RxSwift,_withUnretained,_Widget","doc:\/\/AriNote\/documentation\/AriNote\/220224_SwiftUI,_Property_Wrapper,_Divider,_List,_GeometryReader","doc:\/\/AriNote\/documentation\/AriNote\/220221_Localization,_--_----,_SwiftUI_Tutorial"],"anchor":"Topics"}],"references":{"doc://AriNote/documentation/AriNote/220317_Widget,_Timeline,_Decorator_Pattern":{"kind":"article","role":"article","abstract":[],"type":"topic","url":"\/documentation\/arinote\/220317_widget,_timeline,_decorator_pattern","title":"220317 Widget, Timeline, Decorator Pattern","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220317_Widget,_Timeline,_Decorator_Pattern"},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/220315_Memory_Leak,_RxSwift,_withUnretained,_Widget":{"title":"220315 Memory Leak, RxSwift, withUnretained, Widget","abstract":[],"kind":"article","url":"\/documentation\/arinote\/220315_memory_leak,_rxswift,_withunretained,_widget","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220315_Memory_Leak,_RxSwift,_withUnretained,_Widget","role":"article","type":"topic"},"doc://AriNote/documentation/AriNote/220221_Localization,_--_----,_SwiftUI_Tutorial":{"kind":"article","role":"article","abstract":[],"type":"topic","url":"\/documentation\/arinote\/220221_localization,_--_----,_swiftui_tutorial","title":"220221 Localization, 퀴즈 오답노트, SwiftUI Tutorial","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220221_Localization,_--_----,_SwiftUI_Tutorial"},"doc://AriNote/documentation/AriNote/220224_SwiftUI,_Property_Wrapper,_Divider,_List,_GeometryReader":{"title":"220224 SwiftUI, Property Wrapper, Divider, List, GeometryReader","abstract":[],"kind":"article","url":"\/documentation\/arinote\/220224_swiftui,_property_wrapper,_divider,_list,_geometryreader","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220224_SwiftUI,_Property_Wrapper,_Divider,_List,_GeometryReader","role":"article","type":"topic"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/uikit.json b/docs/data/documentation/arinote/uikit.json new file mode 100644 index 0000000..6b20fc8 --- /dev/null +++ b/docs/data/documentation/arinote/uikit.json @@ -0,0 +1 @@ +{"variants":[{"paths":["\/documentation\/arinote\/uikit"],"traits":[{"interfaceLanguage":"swift"}]}],"schemaVersion":{"major":0,"minor":3,"patch":0},"kind":"article","metadata":{"roleHeading":"Overview","role":"collectionGroup","modules":[{"name":"Ari's Notes"}],"title":"UIKit"},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/UIKit","interfaceLanguage":"swift"},"topicSections":[{"identifiers":["doc:\/\/AriNote\/documentation\/AriNote\/240227_PhotoKit,_PHPickerViewController,_PHPickerConfiguration,_PHAssetCollection","doc:\/\/AriNote\/documentation\/AriNote\/231201_AVPlayer,_waitingToPlayAtSpecifiedRate,_waitingToPlayAtSpecifiedRate","doc:\/\/AriNote\/documentation\/AriNote\/230915_UIImage_withTintColor_renderingMode","doc:\/\/AriNote\/documentation\/AriNote\/230911_openNotificationSettingsURLString","doc:\/\/AriNote\/documentation\/AriNote\/230904_keyboardLayoutGuide","doc:\/\/AriNote\/documentation\/AriNote\/230710_addSubview_ReferenceCount","doc:\/\/AriNote\/documentation\/AriNote\/220711_ARC,_dismiss,_popViewController,_memory_leak","doc:\/\/AriNote\/documentation\/AriNote\/220520_URLSession,_NSCache,_-_---","doc:\/\/AriNote\/documentation\/AriNote\/220318_UIStoryboard,_Spell_checking_in_Xcode,_Quick_help","doc:\/\/AriNote\/documentation\/AriNote\/220307_High_Performance_Auto_Layout,_RxSwift,_Single,_DTO","doc:\/\/AriNote\/documentation\/AriNote\/220228_UserNotifications,_AppDelegate","doc:\/\/AriNote\/documentation\/AriNote\/220222_UISearchController","doc:\/\/AriNote\/documentation\/AriNote\/220215_Dropbox,_weak_self,_escaping,_Delayed_Deallocation,_performBatchUpdate,_setEditing","doc:\/\/AriNote\/documentation\/AriNote\/220214_Core_Animation,_CABasicAnimation,_append,_escaping,_UITableView-Crash,_UITextViewDelegate,_typingAttributes","doc:\/\/AriNote\/documentation\/AriNote\/220211_UIContextualAction,_UIAlertAction,_UIActivityViewController,_setSelected,_selectedBackgroundView,_actionSheet,_popoverPresentationController,_viewWillTransition","doc:\/\/AriNote\/documentation\/AriNote\/220210_Core_Graphics,_codegen,_returnsObjectsAsFaults,_NSFetchRequestResult,_Array_item_to_new_position","doc:\/\/AriNote\/documentation\/AriNote\/220208_reloadRows,_cancelsTouchesInView,_translatesAutoresizingMaskIntoConstraints,_Array_Safe_Access,_barButtonSystemItem,_UISplitViewControllerDelegate","doc:\/\/AriNote\/documentation\/AriNote\/220207_View_Drawing_Cycle,_UISplitViewController--","doc:\/\/AriNote\/documentation\/AriNote\/220128_CoreData,_NSPredicate,_Relationship","doc:\/\/AriNote\/documentation\/AriNote\/220127_UITextField,_Animation,_MVVM,_Observable,_UINavigationBarAppearance,_Appearance","doc:\/\/AriNote\/documentation\/AriNote\/220125_UITapGestureRecognizer,_UICollectionView","doc:\/\/AriNote\/documentation\/AriNote\/220124_CoreData,_Dynamic_Type,_UIFontMetrics,_setCustomSpacing,_viewForZooming,_------,_SQL","doc:\/\/AriNote\/documentation\/AriNote\/220121_selectedBackgroundView,_UIRefreshControl,_---,_paging","doc:\/\/AriNote\/documentation\/AriNote\/220120_UIAlertController,_KeyChain","doc:\/\/AriNote\/documentation\/AriNote\/220118_UITextField,_Keyboard,_UITextView,_ScrollView,_touchesBegan,_Recognizer,_UIImage","doc:\/\/AriNote\/documentation\/AriNote\/220117_-----_--_--,_UIImagePickerController","doc:\/\/AriNote\/documentation\/AriNote\/220114_Implementing_Modern_Collection_Views_Custom","doc:\/\/AriNote\/documentation\/AriNote\/220110_FileManager,_iOS_File_System,_Multipart_form-data","doc:\/\/AriNote\/documentation\/AriNote\/220107_TableView,_CollectionViewListCell,_ContentOffset,_ContentInset","doc:\/\/AriNote\/documentation\/AriNote\/220106_UISegmentedControl,_----PR1---","doc:\/\/AriNote\/documentation\/AriNote\/220103_UICollectionView,_HTTP,_URLSession","doc:\/\/AriNote\/documentation\/AriNote\/211227_Responder_Chain,_Touch_Event,_DispatchSemaphore","doc:\/\/AriNote\/documentation\/AriNote\/211226_associated_type,_Responder_Chain,_Touch_Event,_Run_Loop","doc:\/\/AriNote\/documentation\/AriNote\/211222_UICollectionViewFlowLayout,_POP","doc:\/\/AriNote\/documentation\/AriNote\/211221_CollectionView,_Unit_Test","doc:\/\/AriNote\/documentation\/AriNote\/211214_static,_Factory_Pattern,_visibleViewController","doc:\/\/AriNote\/documentation\/AriNote\/211210_loadviewifneeded,_Storyboard_Reference","doc:\/\/AriNote\/documentation\/AriNote\/211209_-----,_TableView,_xib,_celldidSelect,_Select-_------,_indexPathForSelectedRow","doc:\/\/AriNote\/documentation\/AriNote\/211208_Navigation_Bar,_UIImageView,_NSMutableAttributedString,_addAttribute","doc:\/\/AriNote\/documentation\/AriNote\/211206_TableView_----,_JSON,_API","doc:\/\/AriNote\/documentation\/AriNote\/211203_TableView,_-----","doc:\/\/AriNote\/documentation\/AriNote\/211126_App_Life_Cycle,_CFGetRetainCount,_README","doc:\/\/AriNote\/documentation\/AriNote\/211125_Auto_Layout,_View_Life_Cycle,_StaticString,_fork--_repository_-----","doc:\/\/AriNote\/documentation\/AriNote\/211123_App_Life_Cycle,_init(frame:),_init(coder:),_frame,_bounds,_IBOutlet_didSet,_updateViewConstraints","doc:\/\/AriNote\/documentation\/AriNote\/211122_ARC,_OSLog,_debugPrint,_assert,_initializer,_scrollRectToVisible,_CGRect","doc:\/\/AriNote\/documentation\/AriNote\/211119_viewDidLoad,_LocalizedError,_ScrollView_bar_hide,_addSubView,_IBInspectable,_cornerRadius,_viewWillLayoutSubviews,_private_extension","doc:\/\/AriNote\/documentation\/AriNote\/211118_Functional_Programming,_Higher-order_function,_Main_Run_Loop,_Update_Cycle,_UIView,_attribute","doc:\/\/AriNote\/documentation\/AriNote\/211116_Scroll_View,_Stack_View,_setContentOffset,_layoutIfNeeded,_setNeedsLayout","doc:\/\/AriNote\/documentation\/AriNote\/211105_Outlet_Collection,_extension,_---_--_--,_---_--","doc:\/\/AriNote\/documentation\/AriNote\/211104_TDD,_Unit_Test,_@discardableResult,_Cell_Style,_dropLast(),_removeLast(),_popLast()","doc:\/\/AriNote\/documentation\/AriNote\/211103_TDD,_Unit_Test,_TableView","doc:\/\/AriNote\/documentation\/AriNote\/211102_loadViewIfNeeded,_adjustsFontForContentSizeCategory,_adjustsFontSizeToFitWidth,_UIButton,_NavigationViewController,_removearrangedsubview","doc:\/\/AriNote\/documentation\/AriNote\/211101_KVO,_Property_Observer,_prepare,_loadViewIfNeeded,_NSObject,_UML,_Auto_Layout","doc:\/\/AriNote\/documentation\/AriNote\/211030_Auto_Layout","doc:\/\/AriNote\/documentation\/AriNote\/211029_ViewController,_Life_Cycle,_NotificationCenter,_deinit,_Bundle,_preview","doc:\/\/AriNote\/documentation\/AriNote\/211028_UIStepper,_auto_layout,_prepare,_extension,_CustomStringConvertible,_NSBundle,_UIButton","doc:\/\/AriNote\/documentation\/AriNote\/211026_Device_Orientation,_NavigationViewController,_madal,_Gesture,_Naming","doc:\/\/AriNote\/documentation\/AriNote\/211025_UINavigationBar,_LocalizedError,_self,_Self,_protocol,_extension,_TypeCasting,_Optional,_dismiss","doc:\/\/AriNote\/documentation\/AriNote\/211023_Initialization,_MVC,_Notification_--","doc:\/\/AriNote\/documentation\/AriNote\/211022_ViewController,_Navigation_Controller,_Life_cycle","doc:\/\/AriNote\/documentation\/AriNote\/211021_initalizer,_HIG,_Notification,_Alert,_Segue,_Storyboard","doc:\/\/AriNote\/documentation\/AriNote\/211014_HIG,_----,_---,_Thread,_Serial_Queue,_Xcode_debugging","doc:\/\/AriNote\/documentation\/AriNote\/210807_----_--_IOS_---","doc:\/\/AriNote\/documentation\/AriNote\/210806_----_--_IOS_---"],"anchor":"Topics"}],"abstract":[{"type":"text","text":"한몸이 되보자…"}],"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote"]]},"sections":[],"references":{"doc://AriNote/documentation/AriNote/220307_High_Performance_Auto_Layout,_RxSwift,_Single,_DTO":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/220307_high_performance_auto_layout,_rxswift,_single,_dto","title":"220307 High Performance Auto Layout, RxSwift, Single, DTO","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220307_High_Performance_Auto_Layout,_RxSwift,_Single,_DTO","abstract":[]},"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/220711_ARC,_dismiss,_popViewController,_memory_leak":{"url":"\/documentation\/arinote\/220711_arc,_dismiss,_popviewcontroller,_memory_leak","type":"topic","title":"220711 ARC, dismiss, popViewController, memory leak","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220711_ARC,_dismiss,_popViewController,_memory_leak","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/211101_KVO,_Property_Observer,_prepare,_loadViewIfNeeded,_NSObject,_UML,_Auto_Layout":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/211101_kvo,_property_observer,_prepare,_loadviewifneeded,_nsobject,_uml,_auto_layout","title":"211101 KVO, Property Observer, prepare, loadViewIfNeeded, NSObject, UML, Auto Layout","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211101_KVO,_Property_Observer,_prepare,_loadViewIfNeeded,_NSObject,_UML,_Auto_Layout","abstract":[]},"doc://AriNote/documentation/AriNote/220118_UITextField,_Keyboard,_UITextView,_ScrollView,_touchesBegan,_Recognizer,_UIImage":{"url":"\/documentation\/arinote\/220118_uitextfield,_keyboard,_uitextview,_scrollview,_touchesbegan,_recognizer,_uiimage","type":"topic","title":"220118 UITextField, Keyboard, UITextView, ScrollView, touchesBegan, Recognizer, UIImage","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220118_UITextField,_Keyboard,_UITextView,_ScrollView,_touchesBegan,_Recognizer,_UIImage","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/211022_ViewController,_Navigation_Controller,_Life_cycle":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/211022_viewcontroller,_navigation_controller,_life_cycle","title":"211022 ViewController, Navigation Controller, Life cycle","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211022_ViewController,_Navigation_Controller,_Life_cycle","abstract":[]},"doc://AriNote/documentation/AriNote/211023_Initialization,_MVC,_Notification_--":{"url":"\/documentation\/arinote\/211023_initialization,_mvc,_notification_--","type":"topic","title":"211023 Initialization, MVC, Notification 복습","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211023_Initialization,_MVC,_Notification_--","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/220210_Core_Graphics,_codegen,_returnsObjectsAsFaults,_NSFetchRequestResult,_Array_item_to_new_position":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/220210_core_graphics,_codegen,_returnsobjectsasfaults,_nsfetchrequestresult,_array_item_to_new_position","title":"220210 Core Graphics, codegen, returnsObjectsAsFaults, NSFetchRequestResult, Array item to new position.","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220210_Core_Graphics,_codegen,_returnsObjectsAsFaults,_NSFetchRequestResult,_Array_item_to_new_position","abstract":[]},"doc://AriNote/documentation/AriNote/220114_Implementing_Modern_Collection_Views_Custom":{"url":"\/documentation\/arinote\/220114_implementing_modern_collection_views_custom","type":"topic","title":"220114 Implementing Modern Collection Views Custom","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220114_Implementing_Modern_Collection_Views_Custom","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/211030_Auto_Layout":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/211030_auto_layout","title":"211030 Auto Layout","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211030_Auto_Layout","abstract":[]},"doc://AriNote/documentation/AriNote/211021_initalizer,_HIG,_Notification,_Alert,_Segue,_Storyboard":{"url":"\/documentation\/arinote\/211021_initalizer,_hig,_notification,_alert,_segue,_storyboard","type":"topic","title":"211021 initalizer, HIG, Notification, Alert, Segue, Storyboard","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211021_initalizer,_HIG,_Notification,_Alert,_Segue,_Storyboard","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/211125_Auto_Layout,_View_Life_Cycle,_StaticString,_fork--_repository_-----":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/211125_auto_layout,_view_life_cycle,_staticstring,_fork--_repository_-----","title":"211125 Auto Layout, View Life Cycle, StaticString, fork해온 repository 잔디적용법","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211125_Auto_Layout,_View_Life_Cycle,_StaticString,_fork--_repository_-----","abstract":[]},"doc://AriNote/documentation/AriNote/220211_UIContextualAction,_UIAlertAction,_UIActivityViewController,_setSelected,_selectedBackgroundView,_actionSheet,_popoverPresentationController,_viewWillTransition":{"url":"\/documentation\/arinote\/220211_uicontextualaction,_uialertaction,_uiactivityviewcontroller,_setselected,_selectedbackgroundview,_actionsheet,_popoverpresentationcontroller,_viewwilltransition","type":"topic","title":"220211 UIContextualAction, UIAlertAction, UIActivityViewController, setSelected, selectedBackgroundView, actionSheet, popoverPresentationController, viewWillTransition","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220211_UIContextualAction,_UIAlertAction,_UIActivityViewController,_setSelected,_selectedBackgroundView,_actionSheet,_popoverPresentationController,_viewWillTransition","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/220207_View_Drawing_Cycle,_UISplitViewController--":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/220207_view_drawing_cycle,_uisplitviewcontroller--","title":"220207 View Drawing Cycle, UISplitViewController이론","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220207_View_Drawing_Cycle,_UISplitViewController--","abstract":[]},"doc://AriNote/documentation/AriNote/211102_loadViewIfNeeded,_adjustsFontForContentSizeCategory,_adjustsFontSizeToFitWidth,_UIButton,_NavigationViewController,_removearrangedsubview":{"url":"\/documentation\/arinote\/211102_loadviewifneeded,_adjustsfontforcontentsizecategory,_adjustsfontsizetofitwidth,_uibutton,_navigationviewcontroller,_removearrangedsubview","type":"topic","title":"211102 loadViewIfNeeded, adjustsFontForContentSizeCategory, adjustsFontSizeToFitWidth, UIButton, NavigationViewController, removearrangedsubview","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211102_loadViewIfNeeded,_adjustsFontForContentSizeCategory,_adjustsFontSizeToFitWidth,_UIButton,_NavigationViewController,_removearrangedsubview","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/220106_UISegmentedControl,_----PR1---":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/220106_uisegmentedcontrol,_----pr1---","title":"220106 UISegmentedControl, 오픈마켓PR1피드백","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220106_UISegmentedControl,_----PR1---","abstract":[]},"doc://AriNote/documentation/AriNote/220124_CoreData,_Dynamic_Type,_UIFontMetrics,_setCustomSpacing,_viewForZooming,_------,_SQL":{"url":"\/documentation\/arinote\/220124_coredata,_dynamic_type,_uifontmetrics,_setcustomspacing,_viewforzooming,_------,_sql","type":"topic","title":"220124 CoreData, Dynamic Type, UIFontMetrics, setCustomSpacing, viewForZooming, 데이터베이스, SQL","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220124_CoreData,_Dynamic_Type,_UIFontMetrics,_setCustomSpacing,_viewForZooming,_------,_SQL","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/211209_-----,_TableView,_xib,_celldidSelect,_Select-_------,_indexPathForSelectedRow":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/211209_-----,_tableview,_xib,_celldidselect,_select-_------,_indexpathforselectedrow","title":"211209 뷰의재사용, TableView, xib, celldidSelect, Select시 회색배경제거, indexPathForSelectedRow","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211209_-----,_TableView,_xib,_celldidSelect,_Select-_------,_indexPathForSelectedRow","abstract":[]},"doc://AriNote/documentation/AriNote/211119_viewDidLoad,_LocalizedError,_ScrollView_bar_hide,_addSubView,_IBInspectable,_cornerRadius,_viewWillLayoutSubviews,_private_extension":{"url":"\/documentation\/arinote\/211119_viewdidload,_localizederror,_scrollview_bar_hide,_addsubview,_ibinspectable,_cornerradius,_viewwilllayoutsubviews,_private_extension","type":"topic","title":"211119 viewDidLoad, LocalizedError, ScrollView bar hide, addSubView, IBInspectable, cornerRadius, viewWillLayoutSubviews, private extension","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211119_viewDidLoad,_LocalizedError,_ScrollView_bar_hide,_addSubView,_IBInspectable,_cornerRadius,_viewWillLayoutSubviews,_private_extension","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/211208_Navigation_Bar,_UIImageView,_NSMutableAttributedString,_addAttribute":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/211208_navigation_bar,_uiimageview,_nsmutableattributedstring,_addattribute","title":"211208 Navigation Bar, UIImageView, NSMutableAttributedString, addAttribute","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211208_Navigation_Bar,_UIImageView,_NSMutableAttributedString,_addAttribute","abstract":[]},"doc://AriNote/documentation/AriNote/220520_URLSession,_NSCache,_-_---":{"url":"\/documentation\/arinote\/220520_urlsession,_nscache,_-_---","type":"topic","title":"220520 URLSession, NSCache, 셀 재사용","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220520_URLSession,_NSCache,_-_---","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/211221_CollectionView,_Unit_Test":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/211221_collectionview,_unit_test","title":"211221 CollectionView, Unit Test","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211221_CollectionView,_Unit_Test","abstract":[]},"doc://AriNote/documentation/AriNote/211227_Responder_Chain,_Touch_Event,_DispatchSemaphore":{"url":"\/documentation\/arinote\/211227_responder_chain,_touch_event,_dispatchsemaphore","type":"topic","abstract":[],"title":"211227 Responder Chain, Touch Event, DispatchSemaphore","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211227_Responder_Chain,_Touch_Event,_DispatchSemaphore","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/220117_-----_--_--,_UIImagePickerController":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/220117_-----_--_--,_uiimagepickercontroller","title":"220117 스위프트의 성능 이해, UIImagePickerController","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220117_-----_--_--,_UIImagePickerController","abstract":[]},"doc://AriNote/documentation/AriNote/220103_UICollectionView,_HTTP,_URLSession":{"url":"\/documentation\/arinote\/220103_uicollectionview,_http,_urlsession","type":"topic","abstract":[],"title":"220103 UICollectionView, HTTP, URLSession","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220103_UICollectionView,_HTTP,_URLSession","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/220120_UIAlertController,_KeyChain":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/220120_uialertcontroller,_keychain","title":"220120 UIAlertController, KeyChain","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220120_UIAlertController,_KeyChain","abstract":[]},"doc://AriNote/documentation/AriNote/211118_Functional_Programming,_Higher-order_function,_Main_Run_Loop,_Update_Cycle,_UIView,_attribute":{"url":"\/documentation\/arinote\/211118_functional_programming,_higher-order_function,_main_run_loop,_update_cycle,_uiview,_attribute","type":"topic","title":"211118 Functional Programming, Higher-order function, Main Run Loop, Update Cycle, UIView, attribute","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211118_Functional_Programming,_Higher-order_function,_Main_Run_Loop,_Update_Cycle,_UIView,_attribute","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/220228_UserNotifications,_AppDelegate":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/220228_usernotifications,_appdelegate","title":"220228 UserNotifications, AppDelegate","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220228_UserNotifications,_AppDelegate","abstract":[]},"doc://AriNote/documentation/AriNote/220127_UITextField,_Animation,_MVVM,_Observable,_UINavigationBarAppearance,_Appearance":{"url":"\/documentation\/arinote\/220127_uitextfield,_animation,_mvvm,_observable,_uinavigationbarappearance,_appearance","type":"topic","title":"220127 UITextField, Animation, MVVM, Observable, UINavigationBarAppearance, Appearance","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220127_UITextField,_Animation,_MVVM,_Observable,_UINavigationBarAppearance,_Appearance","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/211103_TDD,_Unit_Test,_TableView":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/211103_tdd,_unit_test,_tableview","title":"211103 TDD, Unit Test, TableView","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211103_TDD,_Unit_Test,_TableView","abstract":[]},"doc://AriNote/documentation/AriNote/211116_Scroll_View,_Stack_View,_setContentOffset,_layoutIfNeeded,_setNeedsLayout":{"url":"\/documentation\/arinote\/211116_scroll_view,_stack_view,_setcontentoffset,_layoutifneeded,_setneedslayout","type":"topic","abstract":[],"title":"211116 Scroll View, Stack View, setContentOffset, layoutIfNeeded, setNeedsLayout","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211116_Scroll_View,_Stack_View,_setContentOffset,_layoutIfNeeded,_setNeedsLayout","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/231201_AVPlayer,_waitingToPlayAtSpecifiedRate,_waitingToPlayAtSpecifiedRate":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/231201_avplayer,_waitingtoplayatspecifiedrate,_waitingtoplayatspecifiedrate","title":"231201 AVPlayer, waitingToPlayAtSpecifiedRate, evaluatingBufferingRate","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/231201_AVPlayer,_waitingToPlayAtSpecifiedRate,_waitingToPlayAtSpecifiedRate","abstract":[{"type":"text","text":"12월 1일 (금)"}]},"doc://AriNote/documentation/AriNote/211014_HIG,_----,_---,_Thread,_Serial_Queue,_Xcode_debugging":{"url":"\/documentation\/arinote\/211014_hig,_----,_---,_thread,_serial_queue,_xcode_debugging","type":"topic","abstract":[],"title":"211014 HIG, 재귀함수, 반복문, Thread, Serial Queue, Xcode debugging","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211014_HIG,_----,_---,_Thread,_Serial_Queue,_Xcode_debugging","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/220318_UIStoryboard,_Spell_checking_in_Xcode,_Quick_help":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/220318_uistoryboard,_spell_checking_in_xcode,_quick_help","title":"220318 UIStoryboard, Spell checking in Xcode, Quick help","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220318_UIStoryboard,_Spell_checking_in_Xcode,_Quick_help","abstract":[]},"doc://AriNote/documentation/AriNote/211210_loadviewifneeded,_Storyboard_Reference":{"url":"\/documentation\/arinote\/211210_loadviewifneeded,_storyboard_reference","type":"topic","abstract":[],"title":"211210 loadviewifneeded, Storyboard Reference","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211210_loadviewifneeded,_Storyboard_Reference","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/220214_Core_Animation,_CABasicAnimation,_append,_escaping,_UITableView-Crash,_UITextViewDelegate,_typingAttributes":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/220214_core_animation,_cabasicanimation,_append,_escaping,_uitableview-crash,_uitextviewdelegate,_typingattributes","title":"220214 Core Animation, CABasicAnimation, append, escaping, UITableView-Crash, UITextViewDelegate, typingAttributes","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220214_Core_Animation,_CABasicAnimation,_append,_escaping,_UITableView-Crash,_UITextViewDelegate,_typingAttributes","abstract":[]},"doc://AriNote/documentation/AriNote/220128_CoreData,_NSPredicate,_Relationship":{"url":"\/documentation\/arinote\/220128_coredata,_nspredicate,_relationship","type":"topic","abstract":[{"type":"text","text":"1월 28일 (금)"}],"title":"220128 CoreData, NSPredicate, Relationship","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220128_CoreData,_NSPredicate,_Relationship","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/211122_ARC,_OSLog,_debugPrint,_assert,_initializer,_scrollRectToVisible,_CGRect":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/211122_arc,_oslog,_debugprint,_assert,_initializer,_scrollrecttovisible,_cgrect","title":"211122 ARC, OSLog, debugPrint, assert, initializer, scrollRectToVisible, CGRect","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211122_ARC,_OSLog,_debugPrint,_assert,_initializer,_scrollRectToVisible,_CGRect","abstract":[]},"doc://AriNote/documentation/AriNote/230915_UIImage_withTintColor_renderingMode":{"url":"\/documentation\/arinote\/230915_uiimage_withtintcolor_renderingmode","type":"topic","abstract":[],"title":"230915 UIImage, withTintColor, renderingMode","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/230915_UIImage_withTintColor_renderingMode","role":"article","kind":"article"},"doc://AriNote/documentation/AriNote/210807_----_--_IOS_---":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/210807_----_--_ios_---","title":"210807 왕초보를 위한 IOS 앱개발","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/210807_----_--_IOS_---","abstract":[]},"doc://AriNote/documentation/AriNote/211226_associated_type,_Responder_Chain,_Touch_Event,_Run_Loop":{"title":"211226 associated type, Responder Chain, Touch Event, Run Loop","url":"\/documentation\/arinote\/211226_associated_type,_responder_chain,_touch_event,_run_loop","type":"topic","kind":"article","abstract":[],"role":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211226_associated_type,_Responder_Chain,_Touch_Event,_Run_Loop"},"doc://AriNote/documentation/AriNote/240227_PhotoKit,_PHPickerViewController,_PHPickerConfiguration,_PHAssetCollection":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/240227_photokit,_phpickerviewcontroller,_phpickerconfiguration,_phassetcollection","title":"240227 PhotoKit, PHPickerViewController, PHPickerConfiguration, PHAssetCollection","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/240227_PhotoKit,_PHPickerViewController,_PHPickerConfiguration,_PHAssetCollection","abstract":[]},"doc://AriNote/documentation/AriNote/211126_App_Life_Cycle,_CFGetRetainCount,_README":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211126_App_Life_Cycle,_CFGetRetainCount,_README","url":"\/documentation\/arinote\/211126_app_life_cycle,_cfgetretaincount,_readme","type":"topic","kind":"article","abstract":[],"role":"article","title":"211126 App Life Cycle, CFGetRetainCount, README"},"doc://AriNote/documentation/AriNote/210806_----_--_IOS_---":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/210806_----_--_ios_---","title":"210806 왕초보를 위한 IOS 앱개발","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/210806_----_--_IOS_---","abstract":[]},"doc://AriNote/documentation/AriNote/220125_UITapGestureRecognizer,_UICollectionView":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220125_UITapGestureRecognizer,_UICollectionView","url":"\/documentation\/arinote\/220125_uitapgesturerecognizer,_uicollectionview","type":"topic","kind":"article","abstract":[],"role":"article","title":"220125 UITapGestureRecognizer, UICollectionView"},"doc://AriNote/documentation/AriNote/220215_Dropbox,_weak_self,_escaping,_Delayed_Deallocation,_performBatchUpdate,_setEditing":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/220215_dropbox,_weak_self,_escaping,_delayed_deallocation,_performbatchupdate,_setediting","title":"220215 Dropbox, weak self, escaping, Delayed Deallocation, performBatchUpdate, setEditing","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220215_Dropbox,_weak_self,_escaping,_Delayed_Deallocation,_performBatchUpdate,_setEditing","abstract":[]},"doc://AriNote/documentation/AriNote/211029_ViewController,_Life_Cycle,_NotificationCenter,_deinit,_Bundle,_preview":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211029_ViewController,_Life_Cycle,_NotificationCenter,_deinit,_Bundle,_preview","url":"\/documentation\/arinote\/211029_viewcontroller,_life_cycle,_notificationcenter,_deinit,_bundle,_preview","type":"topic","kind":"article","abstract":[],"role":"article","title":"211029 ViewController, Life Cycle, NotificationCenter, deinit, Bundle, preview"},"doc://AriNote/documentation/AriNote/211123_App_Life_Cycle,_init(frame:),_init(coder:),_frame,_bounds,_IBOutlet_didSet,_updateViewConstraints":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/211123_app_life_cycle,_init(frame:),_init(coder:),_frame,_bounds,_iboutlet_didset,_updateviewconstraints","title":"211123 App Life Cycle, init(frame:), init(coder:), frame, bounds, IBOutlet didSet, updateViewConstraints","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211123_App_Life_Cycle,_init(frame:),_init(coder:),_frame,_bounds,_IBOutlet_didSet,_updateViewConstraints","abstract":[]},"doc://AriNote/documentation/AriNote/211105_Outlet_Collection,_extension,_---_--_--,_---_--":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211105_Outlet_Collection,_extension,_---_--_--,_---_--","url":"\/documentation\/arinote\/211105_outlet_collection,_extension,_---_--_--,_---_--","type":"topic","kind":"article","abstract":[],"role":"article","title":"211105 Outlet Collection, extension, 험난한 협업 과정, 이유와 근거"},"doc://AriNote/documentation/AriNote/211214_static,_Factory_Pattern,_visibleViewController":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/211214_static,_factory_pattern,_visibleviewcontroller","title":"211214 static, Factory Pattern, visibleViewController.md","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211214_static,_Factory_Pattern,_visibleViewController","abstract":[]},"doc://AriNote/documentation/AriNote/211222_UICollectionViewFlowLayout,_POP":{"kind":"article","type":"topic","role":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211222_UICollectionViewFlowLayout,_POP","title":"211222 UICollectionViewFlowLayout, POP","abstract":[],"url":"\/documentation\/arinote\/211222_uicollectionviewflowlayout,_pop"},"doc://AriNote/documentation/AriNote/220107_TableView,_CollectionViewListCell,_ContentOffset,_ContentInset":{"abstract":[],"url":"\/documentation\/arinote\/220107_tableview,_collectionviewlistcell,_contentoffset,_contentinset","title":"220107 TableView, CollectionViewListCell, ContentOffset, ContentInset","kind":"article","type":"topic","role":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220107_TableView,_CollectionViewListCell,_ContentOffset,_ContentInset"},"doc://AriNote/documentation/AriNote/211203_TableView,_-----":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/211203_tableview,_-----","title":"211203 TableView, 뷰의재사용","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211203_TableView,_-----","abstract":[]},"doc://AriNote/documentation/AriNote/220110_FileManager,_iOS_File_System,_Multipart_form-data":{"kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220110_FileManager,_iOS_File_System,_Multipart_form-data","type":"topic","role":"article","title":"220110 FileManager, iOS File System, Multipart\/form-data","abstract":[],"url":"\/documentation\/arinote\/220110_filemanager,_ios_file_system,_multipart_form-data"},"doc://AriNote/documentation/AriNote/220121_selectedBackgroundView,_UIRefreshControl,_---,_paging":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/220121_selectedbackgroundview,_uirefreshcontrol,_---,_paging","title":"220121 selectedBackgroundView, UIRefreshControl, 피드백, paging","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220121_selectedBackgroundView,_UIRefreshControl,_---,_paging","abstract":[]},"doc://AriNote/documentation/AriNote/211028_UIStepper,_auto_layout,_prepare,_extension,_CustomStringConvertible,_NSBundle,_UIButton":{"kind":"article","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211028_UIStepper,_auto_layout,_prepare,_extension,_CustomStringConvertible,_NSBundle,_UIButton","type":"topic","role":"article","title":"211028 UIStepper, auto layout, prepare, extension, CustomStringConvertible, NSBundle, UIButton","url":"\/documentation\/arinote\/211028_uistepper,_auto_layout,_prepare,_extension,_customstringconvertible,_nsbundle,_uibutton","abstract":[]},"doc://AriNote/documentation/AriNote/220222_UISearchController":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/220222_uisearchcontroller","title":"220222 UISearchController","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220222_UISearchController","abstract":[]},"doc://AriNote/documentation/AriNote/211206_TableView_----,_JSON,_API":{"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211206_TableView_----,_JSON,_API","url":"\/documentation\/arinote\/211206_tableview_----,_json,_api","role":"article","kind":"article","type":"topic","title":"211206 TableView 활동학습, JSON","abstract":[]},"doc://AriNote/documentation/AriNote/220208_reloadRows,_cancelsTouchesInView,_translatesAutoresizingMaskIntoConstraints,_Array_Safe_Access,_barButtonSystemItem,_UISplitViewControllerDelegate":{"kind":"article","role":"article","url":"\/documentation\/arinote\/220208_reloadrows,_cancelstouchesinview,_translatesautoresizingmaskintoconstraints,_array_safe_access,_barbuttonsystemitem,_uisplitviewcontrollerdelegate","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220208_reloadRows,_cancelsTouchesInView,_translatesAutoresizingMaskIntoConstraints,_Array_Safe_Access,_barButtonSystemItem,_UISplitViewControllerDelegate","abstract":[],"title":"220208 reloadRows, cancelsTouchesInView, translatesAutoresizingMaskIntoConstraints, Array Safe Access, barButtonSystemItem, UISplitViewControllerDelegate","type":"topic"},"doc://AriNote/documentation/AriNote/211025_UINavigationBar,_LocalizedError,_self,_Self,_protocol,_extension,_TypeCasting,_Optional,_dismiss":{"type":"topic","role":"article","kind":"article","url":"\/documentation\/arinote\/211025_uinavigationbar,_localizederror,_self,_self,_protocol,_extension,_typecasting,_optional,_dismiss","title":"211025 UINavigationBar, LocalizedError, self, Self, protocol, extension, TypeCasting, Optional, dismiss","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211025_UINavigationBar,_LocalizedError,_self,_Self,_protocol,_extension,_TypeCasting,_Optional,_dismiss","abstract":[]},"doc://AriNote/documentation/AriNote/230710_addSubview_ReferenceCount":{"type":"topic","url":"\/documentation\/arinote\/230710_addsubview_referencecount","abstract":[],"role":"article","title":"230710 addSubview, Reference Count","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/230710_addSubview_ReferenceCount","kind":"article"},"doc://AriNote/documentation/AriNote/230911_openNotificationSettingsURLString":{"kind":"article","role":"article","url":"\/documentation\/arinote\/230911_opennotificationsettingsurlstring","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/230911_openNotificationSettingsURLString","abstract":[],"title":"230911 openNotificationSettingsURLString","type":"topic"},"doc://AriNote/documentation/AriNote/211104_TDD,_Unit_Test,_@discardableResult,_Cell_Style,_dropLast(),_removeLast(),_popLast()":{"type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211104_TDD,_Unit_Test,_@discardableResult,_Cell_Style,_dropLast(),_removeLast(),_popLast()","url":"\/documentation\/arinote\/211104_tdd,_unit_test,_@discardableresult,_cell_style,_droplast(),_removelast(),_poplast()","abstract":[],"title":"211104 TDD, Unit Test, @discardableResult, Cell Style, dropLast(), removeLast(), popLast()","kind":"article","role":"article"},"doc://AriNote/documentation/AriNote/211026_Device_Orientation,_NavigationViewController,_madal,_Gesture,_Naming":{"type":"topic","url":"\/documentation\/arinote\/211026_device_orientation,_navigationviewcontroller,_madal,_gesture,_naming","abstract":[],"role":"article","title":"211026 Device Orientation, NavigationViewController, madal, Gesture, Naming","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211026_Device_Orientation,_NavigationViewController,_madal,_Gesture,_Naming","kind":"article"},"doc://AriNote/documentation/AriNote/230904_keyboardLayoutGuide":{"kind":"article","role":"article","url":"\/documentation\/arinote\/230904_keyboardlayoutguide","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/230904_keyboardLayoutGuide","abstract":[],"title":"230904 keyboardLayoutGuide","type":"topic"}}} \ No newline at end of file diff --git a/docs/data/documentation/arinote/xcode.json b/docs/data/documentation/arinote/xcode.json new file mode 100644 index 0000000..2cd329f --- /dev/null +++ b/docs/data/documentation/arinote/xcode.json @@ -0,0 +1 @@ +{"hierarchy":{"paths":[["doc:\/\/AriNote\/documentation\/AriNote"]]},"identifier":{"url":"doc:\/\/AriNote\/documentation\/AriNote\/Xcode","interfaceLanguage":"swift"},"primaryContentSections":[{"kind":"content","content":[{"level":2,"anchor":"Overview","text":"Overview","type":"heading"},{"type":"paragraph","inlineContent":[{"text":"d","type":"text"}]}]}],"variants":[{"paths":["\/documentation\/arinote\/xcode"],"traits":[{"interfaceLanguage":"swift"}]}],"schemaVersion":{"patch":0,"minor":3,"major":0},"kind":"article","sections":[],"abstract":[{"text":"언젠가…. 좋아질거지 Xcode…?","type":"text"}],"metadata":{"modules":[{"name":"Ari's Notes"}],"title":"Xcode","roleHeading":"Overview","role":"collectionGroup"},"topicSections":[{"anchor":"Topics","identifiers":["doc:\/\/AriNote\/documentation\/AriNote\/240104_UserDefaults,_Xcode_Find_and_Replace_Regular_Expression","doc:\/\/AriNote\/documentation\/AriNote\/220321_App_Distribution,_Hashable","doc:\/\/AriNote\/documentation\/AriNote\/211220_---_----,_----","doc:\/\/AriNote\/documentation\/AriNote\/211213_Accessibility,_Dynamic_Type,_Voice_over,_fatalError,_---_-----,_Vim","doc:\/\/AriNote\/documentation\/AriNote\/211115_LLDB,_Complie,_Build,_Link,_split,_components,_import,_get","doc:\/\/AriNote\/documentation\/AriNote\/211015_LLDB,_GCD,_Thread,_Serial_Queue,_Async"]}],"references":{"doc://AriNote/documentation/AriNote":{"kind":"symbol","type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote","abstract":[{"type":"codeVoice","code":"Today I Learned"}],"url":"\/documentation\/arinote","title":"Ari's Notes","role":"collection"},"doc://AriNote/documentation/AriNote/211015_LLDB,_GCD,_Thread,_Serial_Queue,_Async":{"title":"211015 LLDB, GCD, Thread, Serial Queue, Async","abstract":[],"type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211015_LLDB,_GCD,_Thread,_Serial_Queue,_Async","kind":"article","url":"\/documentation\/arinote\/211015_lldb,_gcd,_thread,_serial_queue,_async","role":"article"},"doc://AriNote/documentation/AriNote/240104_UserDefaults,_Xcode_Find_and_Replace_Regular_Expression":{"type":"topic","kind":"article","title":"240104 UserDefaults, Xcode Find and Replace Regular Expression","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/240104_UserDefaults,_Xcode_Find_and_Replace_Regular_Expression","url":"\/documentation\/arinote\/240104_userdefaults,_xcode_find_and_replace_regular_expression","role":"article"},"doc://AriNote/documentation/AriNote/211115_LLDB,_Complie,_Build,_Link,_split,_components,_import,_get":{"title":"211115 LLDB, Complie, Build, Link, split, components, import, get","abstract":[],"type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211115_LLDB,_Complie,_Build,_Link,_split,_components,_import,_get","kind":"article","url":"\/documentation\/arinote\/211115_lldb,_complie,_build,_link,_split,_components,_import,_get","role":"article"},"doc://AriNote/documentation/AriNote/211220_---_----,_----":{"type":"topic","kind":"article","title":"211220 의존성 관리도구, 오픈소스","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211220_---_----,_----","url":"\/documentation\/arinote\/211220_---_----,_----","role":"article"},"doc://AriNote/documentation/AriNote/211213_Accessibility,_Dynamic_Type,_Voice_over,_fatalError,_---_-----,_Vim":{"title":"211213 Accessibility, Dynamic Type, Voice over, fatalError, 함수형 프로그래밍, Vim","abstract":[],"type":"topic","identifier":"doc:\/\/AriNote\/documentation\/AriNote\/211213_Accessibility,_Dynamic_Type,_Voice_over,_fatalError,_---_-----,_Vim","kind":"article","url":"\/documentation\/arinote\/211213_accessibility,_dynamic_type,_voice_over,_fatalerror,_---_-----,_vim","role":"article"},"doc://AriNote/documentation/AriNote/220321_App_Distribution,_Hashable":{"type":"topic","kind":"article","title":"220321 App Distribution, Hashable","abstract":[],"identifier":"doc:\/\/AriNote\/documentation\/AriNote\/220321_App_Distribution,_Hashable","url":"\/documentation\/arinote\/220321_app_distribution,_hashable","role":"article"}}} \ No newline at end of file diff --git a/docs/developer-og-twitter.jpg b/docs/developer-og-twitter.jpg new file mode 100644 index 0000000000000000000000000000000000000000..63c48359d11d9f755203ce58eeec9d3504a71f44 GIT binary patch literal 12562 zcmeHsWmH_v((d3k$lwHmyAMvVK#&kTxWfz{9D+-L;O-DCxVu|$2^t&*cXtcHF6VvE z_nou8weDK?*S+`0=^wqTYVY0qsp_YztJi*>e?9?FiJRC#OaUfe0000S0ssdA9s&79 z{O|U>2>1#BqN1Xrp#ss+fFKNX3=jb}5Qt4cjE6^nher$o{?-0@`CIv)Jsc(m1|}9J z4i**;J{A@h{y#b_{J&oT{hti*+zkMs!UGTi2yoN@cn}-{2=2KT&<=oud$GK<|Avc# zh=h!a0RQi%f8N6Zz`ry&1b9Rwq`yo^$Owq=C;&J<9A2Wg1e8^H_{tzEZXQlBE*&8V z01pR`0FMkrL&iizczGNi0S*@t079b1!=VCmeRLF8k%0Ke)zCQA;yWAXaKBT92E-FM zbWi3^@ld{1KJ$yMrj`8K%Zpsq)w9WYsfPP6gnz@SdWHdjFVNvZ2%r~B$=}xh^#6&# z|HlzPA$LI~{z2tTQ}Y){G7a$RKheVDopXdRNNN~jH~kxavZ$;1FK{{h6|Om`rX&A$ z<3+?f0RG-Q`p9BDEcwX7EF>DH()gxKR=T&=_@^gc>2R=~ywa9un-C`A;(@Bmndnu2 z_h>hBlSfgu*@}Eq{6rJ66IWwF^mRTr_vl=_L-DqoW92Vtsc4i<1davJtDh4ABjiDs z<9Q?6pR;gcblTeY0}jV!-gL*;UM|y~ob#St47a8_Fyz+zrl{;ipI}e-&ASh+Sv!39 zUXYx3A1-2C+$T)vcoK_|sBcKUD9pD1Oh%$P`Ll&9?dmeIF8Q{m{m!nx^J`K@-EO!D zi0xoI=%yO6Iz`_uRfJ3Mj{Yd$mNLUqwrB1g#_f>rKxTx4e$l`DN4rM$a!eY`sFE+{ ze3!j>P?O^q+cZM?BYcBVChd`g?uo4Y0{T(-R3%kUQ7owX0B9{d!i`Y4(}L?Kl%p_e zM(%<(>*|H#BmYB8sjKT3!wrHGQ@ay`ELNLTw1m$WY)%iQ(ad;mQC~%}kVX3Gp6J>> zHnSuq^T^kVi|VzEFojhd=QBAk$#(DR4)ZKq&cjgZ4skn4-I~o-?utv-yf&DQDX)7f z%!1L40VR3K6${ zi+N<>PhMq{^fX(Abhs|;#(Ob;neKTcx}YWN>#_Z0I_uVzz4zwv9JzhhgbeZamw=@2 zt=i%%x9Dt9E512?AfFtWUU{GA%vei`Mw*Qv#xBW+CBX( z1E#(5fn(X`X#N^qzSm%$&Ze6lF!GevKSbp#7nB$3`qiXR<~-oV;GuzZO253reGYJVbt# z+;v0tYs&c%{XQnfn47>-0kT4hp(~C1BiAg+FH@`5pp4tvImHQo1Tf(9RSrmm{nL`7 zNT6sk=A!4rvi3%jV9vNOv>UlT4Ns?DCmlcKOH6ugT&KHaBU@_HHY~yD)FkgS8!rC) zxvi;ph#Guv6ukP#flw?%0b;YyVOWhHy`vHhRDuGDg6_bZ5LohFjMbcc_%vQ_oFJ51 z&8W#~A>_9iQv@hViAQ~XvA+iouTUP1WQf)Rw;c&iz)AI);Ld}Y5A)0}l^gOb|aTU-I;k?bi9!z?VzVvTNZ==Sb2R3Uy0 zm*EH5t#BQD@32-|P;&XXic}D$U$RgcUh=tgg@GUIAV8y&CaN!nSlb^lB>1nzK3phRq!qs;Wn95gM$7PpYIXs!icO&^sHs zgP4*)zw;wT^RyCh#d0Fvo*TG7;H9;wJxM7UXgp0!JSIp;kyjz7pUnyjj5Ik1|2YB1 zlp_?Ygl?UBi25#$;=#*qt4>q}cwG{#Qm`G%K5169vAI=Kxfbv1&a%8lmogeiR7Dj{qJ&Lfy6DMm;vO+_-C@IhrghAw6ikmLtfe6m5Ose9A?K z!Ycb~l}V840>h_CLu-mAB9vcub=VlNVw0+)D)tUDmQU8o@x_cyG&QrcR$h|qm4*ev ztE1oD%x&0{aDyE`DBQO$T-ovKk6a2CqDbjO)AF1@`cLxH9-JIqCJZeXvMs#mGp4S3 z)wEYAHXvS&t1+jsRkYiBBD-A9Cc)Twb^tj<`eGyvS8%t(@}F6=iGQ-R ztaUxbmlY1gJKdaB(|8ECT8=A$kmB^!Qt?9nCb&O z{=0F`+Ix)d>p+V#At%?(XsEO6&5yr>`a?;WcVm7O49=LQq9toMm3zaM9j_9G_WPP6 zsbGqLbt(vz`NTe}0jyoi$}5_T@n;)*YV0?!L9lb*`GFxjE!AWRuD*hG!#{{Vx*#JA zw9Kg*YagA~VC>yT=W>O9zN1fL!VDa|I*qsgd-kh6+Mrhrk7UP;`!!Y5#+wrsk|l>$TzXE+)A^5MNeuXYZpSrM zn)K;?LX_qwt^0ijdi1(RwFGZvS6*n%q2}K4vbYX7Fq@yKS7VNKKI+KN;&;UuzxWhH z$lLkGO7K%Ft*dLBWhL(BGa!}q_sZHOrvBQZ(RQ|+&N5%A$e$3)tGi}!IUe%&v*qO@ z7=Op%5czeL#jdxIHo;e@F9vP}qs6OQ0?51pQHk(sS}Uy1^~UEVpo7prIM}Iao9pm^ zRemcwOPowT9VS#F zEXq?KfR702Q|kTcVwP=B%_rLIv@1qPn^Gr6wp+1X4eANaotO`2G!bmaX+bIp#1FUH zCx?9)2!8dZzlkKQrv=B1nl5_tW4*ikJStk=wkY05haOh)J|GDl)~Hy!zK7I2ZCi1c zSX;Z`C0Le5Hh*k~m0Oyrb3`wV5R{Taq0Su@xLmpgv#t6u?8>z?Y4M*khpN6lQ(91qvPZ{()g?i-vXmQ;+Jgt1>%RXDo+F-lG3_Dx!E9V&s zWfsczN8cgV&P1hTaIRu;b$tdn6|^4Kvy0uu4S2iMH~Z`L=9~-|>67naZ9I!s=(e`;WWWX-+|AytW84{Q`M z2{01X#r{Kg5cOw&HLU}7W#{daJg2179^o^|l4VISXr&%-hJ#F&#mrPT89()-<+QRc zY**ixLpQ9{HIE#T@yDHr&a8)jzRpULm3lLgEVGEYLA}SsrHea#?qOLxD?_PHQp_KO z^(#uCps1Rvc0L1l4;nba;&<)C>oAg4UrT}!Y0RDI$ra^+6Z>;h|8PSRPvE|g6 zmxA*W+4KC{2y%9`u9H;@O6HT=nG=mVjh48Q7D`RKy6wJv;D*xoa)8XfH|)1W#)OXH>x?0F zj<+JFZYE19obFqdI_(yJ@VU#{*@{`0V%#`;gtn?>*g@FYSYDaspG3qzmfLL#p%@Zb zvWK-h*kkV;JOlnNXyB2>0DyniGyf*T!M)g@0p%1n?)h(UZuHSZ@+2?OkTZDdv=Fyhds1Y0>cle3%G^0}sTg5~iPZ&%nlO2lS;FBkMI zIhMfVlqQ2{JDj*`TN9#(ud+67K@LZYkz;S+5=S?PM z#yq=tAcNVn03sEWV4C^c&!@$kD|Sq|F6LKysEtCu6Vtj(aT2AW3<$#Ed(Qw`nCG5H z;rc<#hZCYBYvxy+&~z}VS~}sZ*rJW-x^K2Ws1Ac@oNHs2$_a0k(Q6o`vxnzCHGeOK zr;R&=DkEP+>fzS9+wL{^*eX?OML!AI!09;Y?F!~>ZV?@3l)Ab0fcA#~qu)(|G-_%j ziYl1?icYglNLbua0+|BofCK)bTKe9h^%0yBnhYr?0#ywx=fk=ciJG7f^4bb|u)Vl~ z5;s6SX&ns1%-C@grv#dz;6!Y`J{Q`yY}&SzlEG^(bnBd}wRSQ@GFv1?bN z#KnN1sm=j?>NAqa6iK&JjLEgyo-Rg!;%(do!>jQ-yK7<6;}829e!_86eyPLXx$DLe z@O>CbZft*ocqOhnv7?e0$mf$lao>nFbctZsJk!ZLT>>0qdwb8vn z=XlV8$3r)v3Z??D6KALDRyJkYRtP55eWwaC9n5C3VvcNIMg0BlmEX8wa8JwhXT05tFQp3A*w3bF^@SwcE7^_-T+>J=ZIP&FpbzLCS-sFgnub%&)1+6 zSwGsfOPS9DEMzRIfn(otfGaU%Uk)VcZW>K>Ze$KH@9QlQ-SaGEHwi07QTKVx&qtNi}EpOPUMYsexTl1D#%e# zjRBjF@u*if?h?H$Dn$2}bT@70s>0oi9WqaKwIP&qdMk{0pX85AX4a1n7}(|>bPzuX zryggDK_+uM)W%|(;+S$AjJSnpbF;5(?c4RPuO9G!sMEWUQ$!JN1#s8-AoKf%A$zwk zTAtskZTc4KGaq}$sdSm1kzsT9tOcM^XKoo8J)yK8=2nnJJwKVkDS3A$5gHAfZH(b%1wxVfquymN(KvKD%n$v;NjUB z$27aTMGiAklmJ{@N?cCvNrMAFAwNco{9=)77$bO$WyA`orL$tC(QpgbUanw)7G-OR z`BqAk&#JTD6uChXhj`3^2AmQz?j3oP>s%@|PQ|n4^c%jy*Ds7gl_YbCX^Nb)BwcAe z`DY7b#I})&p3M6=wK^|((iKQOqse@C`d3Lv4i|gKMkmkxmVSz!EOEZS6G-wHA90M= zuGbD^-hSh~^3cm%=&MgKqOR~Rw5h5|f0EkdFK~iA%0{lfU+qf6n3m=YJbAY3^}P^Z zc!X#sts2uS<18h-^kK1tF~po{S-e5m836$&Lrg_3U2jJIm(#RvwwVt$++WixCI7#j z-pH|hpFAPfZNZ=EGG2K-KzqiT=>%I*N|l`&roAt1n%bXzW7tHlL{!h70gmQnGFXYe zphlr-vKuq%pwhn4rWxzPRO&*YO9Aby53l%_iL2V>)uli?FQ5l}lfytCY09D7z`F*LVxdL}^VFR2|T0W3HcX;_?lV&x9}J zHtKypb+!v*@a~uRrTppN+IgyU2oxS*IFRCJSj=%@8z^mgeF|HLk*7==WY~Tz9CVPI z=a!?)bDv%F1axHFDdl$R9V9~w<)~3@*7(apG8JxMelpjdZWw3I`r6`>n6R9;v+j58 z2ba|9QWrMJMpu!_htq5l^`h=$YSu}Yd0R)vUVGjJJg4p))DC6OfTU-DYCs!tK%4VP z*_^NgYsy&dpS-Ar$5mH1}v&9oqAq?Q%fEZu(T|KKeIrnQlvt8Exgb@zWNYCZn z;MP7~g8TzVD$exTnQHa&%3q9XetT z24{ubyBfdxwgDHIqQ@*06g?EIxl43y{(oUcXGMNKm7TxJk!~zjz?VFa0DqCy&T=D| z^Ped|l-SDKIXT)gtraeyrtXiqy|O+3ZSzo;&JN{Pkp+gTDv_YtFaL45GYzM3Rf-WN za*+@7Uk9oUXqh;Cav_tk-mddy#HZL<{}VW&Vq@L>38OZq-yP^Y+|>ECE2TO?Yo6T+ zRN@F%*y=hd&~89^jrpD}QzbqBLsI9s#W#5c1HihH@hRI#N9!uJiK754ueDC-&Tsvj zS)TfVh95HfRx*pgtMS7G)!jEfqOXh6#aqk>tcthzlAOg|{S?nL)*#Z&&26@3b6k!( z$#}zp*2Pqr-LnE?ybd?%&8?}+yAze3hQY196<9WQ#|89222LX-;&G`#JIMh;SP>Ae z$HtV=fO z;50J6<$&Z_0?$s*oml*_i{w~NHng<2Tem$iVI(pCg5F4u-Tt=ls#q+Fc(nnMc2ie++@ru?b2(dPfE3dd+o;u2qSn+ zh5RPst?cM*wu@?=Qw*Sg!h_T~T85-m6XZ2O>`&{@P^JjafrqZ$O23U&*D5)<3ldZl z4r+jto;TXgV`*z^^iE4n8oQ*C{^Pg5gPBWstJJ1HqQ|8UfPV+(AuhY|x!zZ&Ln2ZO5tv4U^Y0PoZEfkq7En{IS!TTNWV z=G28%7OtF}oht2+XF#;oNHSk)8fifK$am`821{2{X;}0#z<}5A9l4g#azUVGBf5~9 zyg4l`OoBK&%IQZm`BdqFik-`Ht=8AJ@8Qwo0^eE|rV39v3FoBQ<$_v0zl4Segbr@R z?SQ@KjgxK**_x+P(N_|7By+-IS=sjnY(Cc$x*Bu0<0EOX6X4YniILVfh|1a#EMRZ1YX5=E2#Q{_!c zOIsC!RBgJA#4sbJKl7T)8{!6`qB4%7ZawaWV0QjkQBh2XbiI97?XTm6j=9C=GZvjm z2nJ&N{_ioF5k-2mTWC5*uCqcq0eVN%s ztQBm>x>%4NChAxicEX^KMp*TI;Oa|3Xc<1r`^|fXUzWFb?39ryn$PPQkY>sd$yAye zb^=G2Y@)w|C8ixIv?4c{{z+`RV|t@|Sa>$v(ptuA3e0q_Ol*23lljA4HrXuDJwMoA zck*5lLqwXI|E7_&q*bUPR2F-M$!&(!%xj_8*6$bxzrswIaASNac(wz{9kgp_;Apy# z?_+Abb~jEDpg2S6bpg}p)vp3G0Fspxod3vMHK>k+$_0s%tgvNDoR=(-?IsT6i%2Qe zmTs^R-Y`v71ZoT+AxfXdAfpsUFR{ODBKPw823l}A3uAoQrU^LW1olF&f%*nyJFah3 zcnUnt!iI=jnHdUL3&I@^AdG1!C^XxaT zkcOn9#?H*h4RO&bW1D?{im{-bE|b8~`#M=|FKbH|!L4dt@^_2WvC1RYNT%VV5-F>C zOD9{cJd6^4=SyQqO9u^;yR`g95Hmb!N9VCU|MDDGg`|{bg=Vq_3HfM??~)CvwEF$L zcJ3VaQ2ARPkE*`i?{+a-6OY&ARSu;d6yre4v-uLDcB9@aKY7Y5#D*p|dYUNf0Kt3B z4Iq@-X88SXBUs`grmu6HigCj?&-|6)58Xe%`hHbdrp4>fd-B9;IIP3(bsf0g95tuN zvf-3C;R(J<-9;6(syaLWCvgI%=1lEdV~zNh%W`6=zA2W-hg;Hj_LNh5&CFC!i-aXp zf8s!N%@pN1b>U!laIy#Q>bppoo&lKs&OV7^YDsg2Hd8;v4GwP-@>DuDUW+g zoDQAp9TAitNbD?K#HmuFnllwS;qE0E<%6u!)h( z>bn^+>|1(|UVFTiu8*3y^2Q^m5i-t_x&kXY@}q&@ZEeEbN;iuu3*1|KcD%JXki6Do zZ3{;Cde0U2sK2^BIkZbd)y4a@m(pZP7H$F`q#;wv8cF^b2?5-pqD(1^>>@JuxN?Z!6YvvcD-J&eEdPPjstTla_u4_-lf|?|~nyhCFD(ZH%Eir^&6( zk5(=a^NPsTW#b!yXMhLM6B(J8XTh%1#tHvk-%3r_d@bgX`JwK3?3Lv!pVe1^G1BHv z)-#B%y@*uj)kd#M`jY7Pbi2~u#qJc*Y%ORfy1ir*rR%Ky^+s16D+1du^t|y5F(G|Y z!cyUC`)Jx$T!TUufui)74(t^LKvq}JmYeEA&!6sik>(GLf6^^)@7x;~hpdj&a%nDX zIgj}&#UL9@*rm4f6f1UatXHv&t4l&KR8P+OE=2ij*l4s&q^fetPYA}_aqBSCLeLD3 zVRSf$BZbZbzoLJ%MUA_Bu(``}l~~(KG;&5hn%Rc2KJnFCqqrLOW>Tgrw;8)3KGcLS zSznD{t6F7NMFPV@@fO#*qQ5#qMs%#uCx3}M#+W%U^l_6y$9Lh52*qHkkLG{eK9^3X zI0BOf36}8HxMe%P^?tc&5txp(Ce33}vY>Oo!~aqgXq<2*^GY+HLt}jHzY0Ii7-8&5 zwndcURm76F41yoSP0MZdx_OTny@a&`x%qB_i8PVs{nTa=L}M50H}pVR)p~4w$h0cy_oq{lgkjl|h=dS? zlKY6jMZTm$4!*a&AeRsm9k7T4ci&Db9qGOK>32`AhLA{=8A!CBj$>K>_1B_LX1c98 z#f*RrU6&B)Hms6Gt=b^YF1dh;Y|M;pHI7|=zcr{v=riC7{~4esx%t6J5u#x+Wa-cr$ zCRYRR4`@5?_XlNDH#RZtMvRyySx(At0WSCyG?*8v4OH_r5oft* z?rtjH+vy!ilw+wK-gKh{2;#HHL5nk=GFF4b<9=|FvVhPabAq6F+D36hdvc4z&J*V} z_t>qZs2ksk4efB2PYy_7)iN);6S#Un^?#JLvOa?$p}kpTrsn&h!rWx#82)<1s|XRBAx0oHq!B ze48XSJA2Fzai8njaEhw`%G=tXDGpM*3Ot-Hz-8Cv&w%P@z`*fehg(e#-5uCkw}uQn zU5b1t&c_MxE&%%Y>*?6ArnBJYWaE#S3n&$5cO~1Cz>Qe{=0|f~6^VcN#|0-(iFad- znu?Vohny^0onItTBcxS8WD+#Qn1&yS*`${ktpw?YUuQ2rdeq*lKW1Y6gre9XaSfAZ zGk$Mv5sVb~QY!6Q)S=VyxuBIDDoO)^lhe4rY0l-eF=)-;ewe~ho(ZR+cJG~Tk?iQ1 zY3TPl{duNehFlu{{wB}mK*O;ss)a5)%;}q%({Oc`9ib&S|CZ)h>3Di6M6xAxnZpmZ zk`U*TW&d%Y*ddj6*fOw{;%rCvGxp#b>j`1GxK#)NX-QD843BKKs8bL6*&F9CISYXl z%QjyNw6u@V#?1O{yjHNaX98S()^e8d#y$>b+U&KrV}CQu>rZ=0`HH-iFDc)CRHPQe zS@B|KYKaG8nfj-8+-2+%=OzIM-w|8t0zIa^f5Es4y(^N>J-~fGiv$qFcVa6oAfUG+O77M>bkP8@u5+M zdg&)vi7p}m5wTCM)sQFVB&*cy6dyCPsN>(p?H%k|IUu6L?N{QFNmCiIrotf8-M32N z(vuab?$ecvEPI%FG(gat48l-pM1K_}>o{QH>T6K;G~;U?P@~ng*d<@nzxRQW{Ui#i zY>0Po<}w?qk<}K8>sUhHD{s0pvXgkdSBW;uD;ZbUi_Z%g9L#j!Wsl`JySAy$61s5v z>p)wb@A2{4`U!J-fFjF0Nei*`Bb81+0GY6YiZ~A%sg>BM?uF){c#^0T;inC~Ppkzh zI+-rk5=eV(LdP@p6*j?q2N~Ck7c8x*5v7bfACR?;0`@- z61BZc^5M8S)2Qbpc5D;&QyaSNajZvYME15a{`OT&yFYtT5Pm_ZK#mz(nT@}Rw0R9n z*x>k~c96bduVS$)2=--+FpXVoOm>;LU%Lkk;tt(iO$`kmVS{cwnj%vKD! zG~3nT`+UOEz#y;MWTs>v(+0P!JEWSBqm!?Yy&&%8I%*=sk>*mzUcRSzP@zP!y9FLs zjE%C~m{kli;zNe68qQkX_7`nS%TSvIMmZNw5G#eHrF=Yw`N(&n4w<(8=q#e!X^+nS z@;&G#da}|>?(Rg(sVF!_Z=@S8p!fdP zKX2wYo^Sz!?KkK9V}#;O+wNfP4Md@E#5Ka71gaCkpo&rBwM0o;=3Q<}50YnAE9YYK z0+VI}8`k9)Sv3Z1*ZCD6CPMLf14ueKQTJDz`M*G02i9lpgBHa>?Bs|aadl+?a*&2k zi?F`j@(pZdfchS*+xM;H-Gmd01f++qe(RXdnJ&f0r6S)|sXt z+zr!!OZvpX*Z1o}OWKqrFA0V$IIV^<-D6nzUh%<*tRNzWfIGz;{_W>rL!_eBu)bfH z8nU6FJR?-MkktCdaLdq*o0#+`%(V^49C~`LluUt?biR8<6)_pXcwE%D1oKO!lSbo9 z+w(KNWEPDamJovanRkv+@A(k#R)MZ5`neGJm7+v#T{L;n1*X?ca|W4WV`PF%`+l&I2go>i=hHbvD8=t^3 zVBp3{Glf?Zo+&dz7NZP$*JXb4?$Gc{=w_&A4#bBfLM|&`CygN@+!@R*a?L5Qq_tQw zqmTIItFN-8eeVI``<9d~`mztu3lb@`sy3Nr4f>vDVVDKU-8&LWy9eni^o9CVjz)WfC!kNQ+~~5B8k|5 zu1Cn}=D|SYWFw3+g1;^N`cIYW6uv95(jsyns(>_sImawbDvss3$lPn0{IPjD=6k^$ zM5!SPEnUpyRXZeKLRyNU2CHzFqNKu`&EadJtiWl+msVkgV+#4r;@^j=#mGGtLrgqgB8=Ft zlQ+^d7z*X)UZke#MSa=NB~g1#L7<-qpiqPmD#pPes6fhZD8bX~Plj^#Z4XvpcrXqP z2~Y-KQU(In$ThZe_DZaV$lRuCo>#Y3PnDN1kVPN?DTroG4bWldEKTB<2@?S%XG)~^ zm~U2=mxKH_n#Pmjcd-%cZ}`TH6&=lDsLw+jS{jw<90oCnLFeyqR4hVL(l?C7GkrLK z4ZCvnP=?V`E@o1FNTERWEumNW_audo~xwDc%=6Z<4&dLrn1BApb#B|GWyo{sSKohy?uOX%YX&{U7-s z2L8jq|91>fdM%B$v-Z9ti7^n3TPsL3`HKN(VfO~+(*?fzf9OEO5*i=+^0*U-vE<69 z2MhYg`?m}bb2IgNDs<@sh>4Bu59)ojp48J5|F;JJrefZ(Fvnz=FeU6?zSp@%8(U#k z5R%Co_e|q0z+uqWCHvZHc!+=S$bNz_7x~#jHfFNASC%6CRm+&sc15Sa@vYQ z1`RV4s%S5kW2LO`-Te)Vk-4#SPFCdp71vF{_;A{d*JC7RtC3Wm7sb?pT zTm7bsdHR<8ddpyWjr~31@7IJqley!2wT;%?ExWx;NL~2(N-;MxlO$h2VRb2K9aT@> zUw5Bge^;+^vuHdhzLF7gtuvz0sl4O<@~t` zz6^ys(MW!9k>aDf_|wqGe;fOMsVvLYmEAndT{|??HI4;wmykP;DX6thMmUXsUl@U) z6M?~PscQj?!d+zO%J?BQyRmw+|6VgywF|xTZ-HpN?XR`0>)P(ImoqF^rF)Pua_+=#mv8!ChwhZA**TseoupOjAEOcSnJ;<|K@jb zne@K!ihG5Z#^R)+v0KZW0!YqJ(#T>wA?NEZ221pMF-7mt3GjyqR9X45HfE~i^xZlU z5PC&NJpiQrt~(@ap{d36>)Z_-zLVMP;KCs^`*W%^^F%NnVlmrV^%F znWbS<`u~OTC&focKm;J503HoT{x<$xIR4u}_}h5@CHJGtAdL^<-Kk_gSpUl`cc>_k z^joQwJ$W6K?pK9(a8CpIRes;EWfKlC)-Ke;W?!)+%PwWAopiyS8`qUOV;%d2rmfOV zZTQRmCF8iFDjRDKI2GP4MQMWMZ2Op@L1st;HqIj({NTzGF7K1WtVdbyQvS2E?|!Bw zE6sOt$0y(!;B*(SzxmDLAs;Pdy$uTps^F{j{bk+W+NrFQwt_hkqmROA~@k%)Xcr1~NTU}*0 zxXF&B6eEjEr|NgoPl1W$CWT}SccBrJRp*!~7zcAzKA%rnUs*-ORe~_`ti82L9#rSo zzJvYDOF3KCR`Aae+QxLgQog7#Q3hO#=x!&%!QlmT-Ir=$Bx-8PtFNhC$>I`+-AuF36 zL(4DMw1&9vqL|{)Cl+IGJ9!C*2Xoi$?Fz$z2Y2Z}j&E}V4!!Dc$`e(ZjlOXux1a@# zzagc2=bBsaE&p5@@OQ^yll#EU>G;7gfLr0tc;M_oEM1Kz_R>~laW+0$Vvqxop&as7 z-#4Xp0*V_UlNRC&;CBCJc(pEThGd7{ke(iCvp&CsRe-d;j1W$3RE2#^y5i~cnrysz zc-^OrsPJTIJX-GsRdYdn_D91!IVIMiDKWNPtHS-+p&L0Y%MYun4X*^kH>F!pnXnvP zG*_~|+rCdlq`tdMpATgp7{TT|+Ekza>?6x}o#=D_3}F2kDjZg(HLgdh({l9Lh&ZNM zvX9w}0<8sZ!DmIK#3qy0l&I-8hnh5yj{NCvk0no3e>Szdap0^uXjQ4%cBxLQD~32~ z@_k>_szh#hp%=_Q%LeAnTMuq=!UVR8!1_N^_`UifS;4LEoZ;y+#NG=MxHC)eYQ>x7 zP4l$S*FK?s2HX%ihH^)4I1%*7l{005Oot9vnDMg?BU!tYy1JeL5k+E&X!mofcA@MQ z$Y0`9Uc9X@5~>oD2nk-Y!yszDAINzmeV&8t4Jv#qjJoZGQ8m}fl zOYgFTQD)?`(mXrVW_=?){W_Vu@2rWMaR%c~-JJ0m0M}fg3*@$SSjrnR0dw;huYg4U zv_hQQjmYfkp^n>Ks1wmWr0Tq3blHPfp~y=a`Db>Cq!g~z@k|+Z@m?V&d=@*@bKkGY zR$|rn$TwHK{1bp13vzwznW!t|ACgx}F)OfP_7*l&(D7c(?P!|8N6O(-jB|!Z4OY3& zi^ymP-}liErRY|_7A3B4fh)voL;-rwX7>xWrbQ zXOU|DNa&-@*QLBcpHPP$2ZL!5r(urYsU9TZ3)GI|y|d-tcK!a4_a4o@QqSHgX#^$i9z>6Ww&ekUuxK7|FH9pdAfND&Xmrr61;vI!({0Lp=&$XKtt5W;vv!@>wRP?ahM{br+hlo~UPC)RMbj6S zCBHQDU2_IAs_PNH!WQVsW|haq;xERe8U&vRl4gy_Axx#on&@8Y2G!c`rJsb?0{qNZ zR$LC}0-LknO$%jIlDj?FOwL6COp-RN^gri+G*tV7>Ac*whmTY3VKu`++6i!$cPr)$ zres5@zNS|}f*dt^^d9{#(v5%s>2nz72Al9WD+gAk$hQpRlzCAy{@=X4U?^VoGH2Kq z#wN+eIfjvXHXK*C`I$`q&s{yx59jP+cPS(B>mQo>TYKskhgjEsSx6|?JCe2uDJ5ml zNHr-dp&rt2bE0`1PSf(*I;DM)P>vJE4y*wMe@uG3$0~=QG%vgz^lyH1OrcTj!0hwS zRD0fQQasds;IEXmq4^n9>r2a$%%lm|jY5CLYNQxR?58%L312gvd3==F4H{4Ok|Ykn z_fH}k<-F<(|9k``g(?-@pPE!Eqt?{ZN>Ks(MY#<$=DjKdQH4tUrcTO#1jgIc8Kd-O zSfL1HBPTY;tCj?Vl(r&`4SNi0MaoU!q$j{&eFQsWvbQOE2ECha)DT;}W`Kw)1`tmCCo%Xp9RcBw{~1uiWq9)u zhnKfLIV=vS3AWp+UTTs*Ki~*B4!k41BI)($eTWl>B(_9vR*Y>v28$uNL(e+eL|KSc zq%Q3)dUp!v6juy^u?x^yF7pPIdIzBkkDcI+XBf$6@gWEjkLHf zo3{n`yQBXKKc?vgxfl!Om~@5`m2E^Dyu;HrmtWZowchY$eg*($?ST0y7GP%;Mf_+%^8c`VBFdhTpeunonA zMM9L69*rBUU`Y4{?Z~;mid#%g>~J%`)PO?`e=AMB({98ISyA`#1ibw<%9`219!~%% zDkt6^S1wU3p<4e?3#P^{i5;Mm$5j3MI)`BTi7)q@;kz<#{>`_+XTTWz{>B>?W-wLR zpHWRl-$6Fk5IQ~^AfmW(IP-Fyxa?Ivjy-e0DWFC|EhVdh1S_ZSN1l*AnLS?6*M6(} zB9?i!kmC0CDGj^y%bcsH`PX%uh`(U|BG%u8AAk1_mHEkeqr_Y8wLZP9ud3VI)-R2< zKD;EG)tt+F2An;X4M%ncu!~~kU3_b@ucW1=S4E7J_9&?=#mJjcG>(paWE~f1vFcEL z-EW7VgXi0=WHblz_0Fo_i}iKRiH}To8{tGd(jPsCFxT6rX-w44_F0rCeAuid+u6ju z*n1>WhVs{2pls}VA~S44?P>?@TQuX<8ge7zoR?!-!y?o3FS-oh$#k*6LK(Cr0B9gI zvXVSEG9_f>Ufb9je%+2ERtBjWd1*vwbjkRocxNRs!f(-`yxby#Ym2cjOALr;+$cP> zA_tB6Z)QKYDA^p;nobtZn*+cIzO3dB!S0Ua8ore76r2>fx}lmV2lL!R{BzB348nf- zO>Q+#Z9kran)xJ6kT#wU!1P;CqJVuNL^EIO00sugoSn?TgPqLv`>$p^>-z8aY(%YH zgFe;dbX$KTFp}QUeFoS+C{qRcj4HLXe`((D0A9F8wST1SCU3TWGPKN-fZ)Fq*e6Sa zc6LPno*OYrw>isc+L(HZ>sTLiLSG6DY|4syDc;vjpG&BMdv^1*gxS?>E(G5}Eyc5n z@Pazq+m;w9HImt|at7&IxQw9aHj^5tQ0nUH@ds9Ob#!<0#*OZMj&&=L1Q$9SdFr0@ zic$GbECj0(DR4QlgS|E4FbAD57RT?x<0d2;p>$i6XOJ@guZ|mA+8)&rYu$7)CO1*? z%mjlQ8y0qgg&XUZ^rpMHkO_qQ#S0Nd`D*fAO>UOvda_FT(sq^zuU|;r9lbK@neji# zf;~G7`!T#ovsoG_>^03asx3{6C_TV61$4V*4|QtYEwAB2JykkIV~nZzV8r4|YCb^A zLe77Kh-(wJ(%on@6=R@BQw!h=I#fPkiPx z=gv>oMgV1{mTt;~*R@hkO+WlMP@je^xIR#a>{MS&X$6YkdD-u-(eWLSndumo1GlpJ zi=^@jEJ0xGQjC4dZE&@^Hbg(rHlc@gVN1}mJNJ>y<8V(sNV(&6z?cPXHWK1lg4d`T zZdvAZ&p>TW0(oQ{M3H(1Lq<|H{>|953WGk~cT58|^|vEdHoQRS!0xVou3b9kMbtzT z@5M@g&#Hdb3QdNTHoVpGSwT>34EoW7)1c{%!^qAL-GJuWZB5u<~HO6B{} zsGQP*1g3sm^2dN*we~~~16cm6!#eQXv9Q?!+VTzqMXKW@qsno?k9NfU2D1Eq$s59_ z5Gv@8j4y~^7Ta7IxHAxNuP%MwJ3hordf`1 zJi>harYwNYAG1*pPQ)=a+U-uhQ>r(L!#@o9dL0R^kQu4aXCo48$bu%LIKB+jxTPE5 zgdDC991n$(B~vh&GUV&Xjt}+Rd~|!e8d{s_ti+8KA+jt&sNSMQnv3ZZo;1F;GKT@SK#k3L+VA^*-?u&8FDIXMm_Z&NY>pTe*YV zQfFmLYl1MRHu(zYDR?V5TN{%Zj2It`;u@P8MtcoT%kD$>l-nW`AWM{4wn#0E5`9!x z8tOl*k-4u&zpAA+UZ^_ev~TX1cQAWh(Hx2&eJAFIue0On?X-tKp}^)iU|}IfeS^9w zf@+dkzHU_Xs-q=qO7_obZmg3!T;y*)IXOn8mIVa8pN9+y(0$H*>@QdNndQayaYoFwrO(WHUzI6V>BT#X(~4Rpp8kjqr3H6L~VSjK24Bo^_I^Io8)fy7iu?$ z+S`+?oZ2nHeJNSykXn_bqz3=}E&X@#cy12{exTR8y_O1jT8~+N^SEB~g#tYW+;>MS zL$j4r-`?d_CZ9-0ma!*`_O5Jiz%4R6t*|(#ROanol^0e&k~hJta2h_n__VSEHjx(P zJo`y{frqxBtKQs|1?PGhw25UY_J2vTVcRm^aTp<5hzje0UptX)0nuX{#< zBmx2$S$wx#N!}6Iiau86ppIMn6tF#S)=39(i1CN<{_@g$$u^Z``(hvgv&4+e_jSRd zm&!N3)cIQ5cDpH!xQT{l?V$oGd8aO6usqu=CyNgLptS7cGeDR`@I&d%jQP= zziBlXFp-BP^_Y=i3wX4Jh~)kvN%-^(Ao0=1to^vmD}76R7?b!(|J^F|ho+@t*|g@{ zTd>qz#@l@3pXZJaO^Sv*C>aUmHhhE{s#*c@x+e<}%ijo$?aq@q!8D{^9@yS!p@-EK ztA-ONS)e{Al%`yfNS4I8;S#srH;=EY|6KDJMYY&|+VAiiauT$kXt)9dp8==gYbzfy zW`LhM>7s41-b?$LQrr9rjt(Lv&6;wFYqxCjJX+aJXAPh-62HO4idEddgvIA=_zJW` z$6~WP?Zu1Q2$nDBZ`Xp|1&HAB9$LZtc(_};oS2GN%ld&9Xu*2J07P18RIaF~&0t`h zm>NpoA-R)6a040el}BFsyI1_~5dUX@Ql@5!uKuCqTa#PnD8{vX`3CrJJ6&r`*21$d z(@`fO6>tR4;zjMB&aQQawG@0K=*=N$Tkq;MUZpVgWQh7FCO3eq5X2lB%PTjUg|SQ zl}~;#Y5m*@y|AkwKi(K7fp^`i!>t$5D-&WMA^?I90rr9EDOLMA=#lt6`u2AFv-%;t zzr;Td4cE|t+}*Y{xXdC5+o^*0=4ZLYEGLGJbNeJJMomVi@o>1os^1WfBY09VMhh># zZJ=`0rD&|HVoKkd#%HQz-i}rIZkm{>C1(qZXGerGg2^CYELF{6s;x!;O(?QK0WdDi zBmxW;PW~LcWFM{SYh!{(OE(!K2#@!6o>WYmx$&JaIpfP9X%Te06QM8_cdZB2ZvZ`6tW8fQ~g!4DtX;hMvMBQwd^=M2|Y^!Bk7_UXw4_5{_(5* z7o+cZz+_%ZVMsPnDoutHTn3B%mqxv1>u^y;<}RKIow^u!GjqNBB`U9pB{c^-_jFe_ zu4|tFFN-xQHtFQc)mN2*0#8Nu+xZ7O_wj8pfkm+_sDxyz`W47#dFm_(y7&uIWhB@L zCFEf;vlqu2t`KLM09G;U=!N2EfNxt{HtXfqw~> zAkPHsJ;6E@M&CbHM(#P7ku0znxE2VC)lzn&$`pUdilxyA@E}AGmiGifwg zBW&Mgfb8iZQ7jWLlbnlW2R3Fjcc%YN&H9Aye{`0Nv8uj~Hk-2Hjg31LQ#MNlm%7AU zLpwzx?-H;I5}&x`&axXxjn^R?i5-u-JlXr4bB2o}zbX-m$}M*j!<_KH0X^vtNA>ui zN*m`o2D5@E;}{$|mq?!t-&;PNp{hPn^cqdGHkh3qUZ6Gj`waxssUkwXl?33$^q)CE zVcY>x*f-(5%931u%WBzEz)7WtO2 z+Mt@BCL6eOBrx|rytepX=5y0sICiiHT4ab75;AeI|Ayx@f`i>@I?gdEZ@A`Y{{ojM zKcwweVeohOk{!sjqK`PinK83KHtoEl?R<%}yvjH^ths z&xLFCE4i+Lb4#1M_Bf-?ACBp`Vdb#ySI0=hOtkC{zmR;W*U)F(_mrAy-VkP2MAPyn zjD^3KAXsi#-ZNQqbSJq5g1XwIiDz7Ft#OY{r!F}8%IE8d7PHv`b|;8Rbm!*)paNT! zNlZMgZt-DnY#7;;YyYo(tocR3HYE$#YxtatzKGTd9$KOFhz4vFp5Z7oh#|_xaF_X7 zCN#B$Pj2wmQ(;mrk;ZS@Iz%7*Q6QNvZ+>uChBPg~#G@v1`IGOPcIM-?Z_d_^t6HRp zpW5SvPQd&Xc0{}`CCa2poR6gxu*Xk#?VsAo&^rw9L!e$bAp=45?xL}tgI8(W?i6sz z^G;ZtlH>?y`Uw7P^Q@)Z$>8n&&tBbl;cbP{oWJ+zfolrRbVKVDY4O*M60lFDf^RAj za&b;bvl|$HWVjM+;S%VM0D8~qs2@4b#_CYI>a-Nf@Qi#m(EwGdU1YeZ*X*ryvn zQ^vh+VBIajlJ2ak&oVJWyHiu277o?vRHRF*M2X%C9;vD$e(aSAm@lCamsyURt2wD_oCRRmO zfR31Dh1&Pb8?Q?%pTP0;WxmuD3%5iHkM^w=sda1X*|q%6;M7X9R96Hjhu28)yn3y7 zUKMAVwET%)aLS9=aAi2{NCDpTz#q_)rwiR!u0DN{@=;e2voQ-8l+%5?J$S9ew-q-Y zLN4AueV*ia_R>#<_%Q3eUY&rB<=ED$S-{tGv32soT~BsQyA%UtEOy~#k#qqM?Hi*u zk`D$GSd?VNWi2=u8;q7c8@S(AHXUGUBzzX~9j&665grDqKV0xWPGBY6v*EEdi@a5| z%=PL8{%)U2$Zj>JZod7XOu6HMdLR`YI#}luFm;sCi;q4 zx1cXs|DF=4!(t(kIp2-egIOV?)r3^sUj68ezY!bH&9Jthf`iMWEn^c6?4T!)1AE(4 zJBb8?BOTp@Y`dK-)=*5VBbH^HA%@#r2^E3xRevb5Xu^lVvqzfW{^5bXeq15Y!E%5= zN%K6Xl69R4|G#H(jR#_I?gq;=K#X#%7(bMi;!qRgQvMQFRdo|%S!IE8S&q}8Xd&m7lsU~GBN;F;ja2&QbCvrsjezvY>+;HyM(C!r3 zdHe-vd7;PeNn$wlOzY`~7wFZBePxv89%I;@fO1x)NKGx9T$Xo}0EGKxG;*=-{TSc5 zS^4UoN8zTkvN{=3P=VsBJocF&Uq!<@^$=uZ%$@2uIfq@}DFNJ#0kW&%KQ|I@~nd_K-mnu4(FY*^1mrh1wpSl?gHw4rrs9KM@fh zxfV{uhoHE7y1N2Lqfz3nDiFn{2lcEy`t9=8`)0|?VLJ;d@Bblg`sKY>Ra_c@VZ2lTGKzUjOlHE$o!IUq8>UjIdu4>fmiWM!B45r;3&L2o|c^?Dsu$>^^%ZHJed$T^leZ%Hva$lbYh@wNk||DNqb^ z^>JaBGTR06hWCd@MY-)c4~D06eTI!SlIN};Ru7^8nY$H8P1@BPH~-{h+5U2_FUoqW zx?eJ~%fG2vaf)Xc)4l6{r@0X6&fSl#xB$EiWnx9yy;M-|(CBB!HX&f3Dq6Rg+`?Ps zN^JVcHG3}k+Y^UXb=V9NVv*+O5u%>ATl9(aR3eQjPxKbZ-&rkR)XJnKBT9jr^t)+r z&Dy%KyQG$>A};f!C12ccgZ*lcvYLI?nx2}B6p~cQeRpnOS@}2#tB{KsMFqQt;F2X| z{L;Ia&nK9p&-^G1<2Ad_9f=}co3CXQZa@TzDIht;dUtf<;`>jWqFd%5B)!1ug=?>D z7_tBP=keCCjLt%J6*TqH%BO$u+Fu9eedY+_RHeqUsK#p1FSQ{M5l*HWD=U+Ol-H-SKE4wIAPsOGg+#Z(Lkaud^TZijlyVZ6?mJM84{Ys&P{#-j+%cTL{_ z5rs%;bDDSzs-7$6&2)4mAc}ON(=(xRQBK+DkP4QnZw`L_8Y(BXr zKt$+#6&$|E)yU#@tm2kIIhol!uU+0fHQ9HL0f=JKm11H~6X=*-9e8Vqg4`dK6*Yww zf>jiDIQq$TlG7OrLP}OGXTbGZbfE5pgTg^x5sU!k2I0RMYwdABQ zYupmGBBvmfo||f7hgIJwqVSSMD>Td;i7D+p7pkApAl2cnwi#jgstd8StQDR}f58!2 zLGGR{e?*)ufogl7u zuP9uJr4>n|25v?^l_5_uKGbNb=g{@{L_LVlFyy|5SkSgmsbsciPFfitPq$ce!%fKp z@@dA`Y@`+1fg8Xt(o#Z`YK({FxW?J{-TH^O2F*b8?%bC%E?Qk;d}v#MAfJ ZDocumentation
\ No newline at end of file diff --git a/docs/documentation/arinote/210803_for-in_-_foreach/index.html b/docs/documentation/arinote/210803_for-in_-_foreach/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/210803_for-in_-_foreach/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/210804_-------/index.html b/docs/documentation/arinote/210804_-------/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/210804_-------/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/210805_api_design_guidelines/index.html b/docs/documentation/arinote/210805_api_design_guidelines/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/210805_api_design_guidelines/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/210806_----_--_ios_---/index.html b/docs/documentation/arinote/210806_----_--_ios_---/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/210806_----_--_ios_---/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/210807_----_--_ios_---/index.html b/docs/documentation/arinote/210807_----_--_ios_---/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/210807_----_--_ios_---/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/210809_enum/index.html b/docs/documentation/arinote/210809_enum/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/210809_enum/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/210811_swift---_--_let---_----------/index.html b/docs/documentation/arinote/210811_swift---_--_let---_----------/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/210811_swift---_--_let---_----------/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/210812_---_--/index.html b/docs/documentation/arinote/210812_---_--/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/210812_---_--/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/210816_----/index.html b/docs/documentation/arinote/210816_----/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/210816_----/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/210817_----/index.html b/docs/documentation/arinote/210817_----/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/210817_----/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/210820_------,_----_---/index.html b/docs/documentation/arinote/210820_------,_----_---/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/210820_------,_----_---/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/210827_filemanger/index.html b/docs/documentation/arinote/210827_filemanger/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/210827_filemanger/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/210831_result/index.html b/docs/documentation/arinote/210831_result/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/210831_result/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211004_---------,_--------------,_git----/index.html b/docs/documentation/arinote/211004_---------,_--------------,_git----/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211004_---------,_--------------,_git----/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211005_git,-_-----_--/index.html b/docs/documentation/arinote/211005_git,-_-----_--/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211005_git,-_-----_--/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211006_------,lazy,---,----,----,-----_----_----/index.html b/docs/documentation/arinote/211006_------,lazy,---,----,----,-----_----_----/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211006_------,lazy,---,----,----,-----_----_----/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211007_----,---,kvo,compactmap,----/index.html b/docs/documentation/arinote/211007_----,---,kvo,compactmap,----/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211007_----,---,kvo,compactmap,----/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211008_destructuring,_commit,_branch,_changes_requested,_refactoring,_-----_----_--,_naming,_reduce/index.html b/docs/documentation/arinote/211008_destructuring,_commit,_branch,_changes_requested,_refactoring,_-----_----_--,_naming,_reduce/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211008_destructuring,_commit,_branch,_changes_requested,_refactoring,_-----_----_--,_naming,_reduce/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211009_optional,_dictionary,_compactmap/index.html b/docs/documentation/arinote/211009_optional,_dictionary,_compactmap/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211009_optional,_dictionary,_compactmap/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211011_condition,_condition-list,_flow_chart,_------------,_ground_rules,_daily_scrum,_----/index.html b/docs/documentation/arinote/211011_condition,_condition-list,_flow_chart,_------------,_ground_rules,_daily_scrum,_----/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211011_condition,_condition-list,_flow_chart,_------------,_ground_rules,_daily_scrum,_----/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211012_----,_properties,_keypath,_kvc,_ios,_hig/index.html b/docs/documentation/arinote/211012_----,_properties,_keypath,_kvc,_ios,_hig/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211012_----,_properties,_keypath,_kvc,_ios,_hig/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211013_naming,_if-else,_switch,_----_----,_reset,_reflog/index.html b/docs/documentation/arinote/211013_naming,_if-else,_switch,_----_----,_reset,_reflog/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211013_naming,_if-else,_switch,_----_----,_reset,_reflog/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211014_hig,_----,_---,_thread,_serial_queue,_xcode_debugging/index.html b/docs/documentation/arinote/211014_hig,_----,_---,_thread,_serial_queue,_xcode_debugging/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211014_hig,_----,_---,_thread,_serial_queue,_xcode_debugging/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211015_lldb,_gcd,_thread,_serial_queue,_async/index.html b/docs/documentation/arinote/211015_lldb,_gcd,_thread,_serial_queue,_async/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211015_lldb,_gcd,_thread,_serial_queue,_async/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211018_let,_var,_tool_bar,_design_patterns,_architectures,_mvc,_dictionary,_downcasting,_function_types_as_parameter_types/index.html b/docs/documentation/arinote/211018_let,_var,_tool_bar,_design_patterns,_architectures,_mvc,_dictionary,_downcasting,_function_types_as_parameter_types/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211018_let,_var,_tool_bar,_design_patterns,_architectures,_mvc,_dictionary,_downcasting,_function_types_as_parameter_types/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211019_nested_type,_operator,_dictionary,_sequence,_localizederror,_error_handling/index.html b/docs/documentation/arinote/211019_nested_type,_operator,_dictionary,_sequence,_localizederror,_error_handling/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211019_nested_type,_operator,_dictionary,_sequence,_localizederror,_error_handling/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211020_kvc,_notification,_notificationcenter/index.html b/docs/documentation/arinote/211020_kvc,_notification,_notificationcenter/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211020_kvc,_notification,_notificationcenter/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211021_initalizer,_hig,_notification,_alert,_segue,_storyboard/index.html b/docs/documentation/arinote/211021_initalizer,_hig,_notification,_alert,_segue,_storyboard/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211021_initalizer,_hig,_notification,_alert,_segue,_storyboard/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211022_viewcontroller,_navigation_controller,_life_cycle/index.html b/docs/documentation/arinote/211022_viewcontroller,_navigation_controller,_life_cycle/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211022_viewcontroller,_navigation_controller,_life_cycle/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211023_initialization,_mvc,_notification_--/index.html b/docs/documentation/arinote/211023_initialization,_mvc,_notification_--/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211023_initialization,_mvc,_notification_--/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211025_uinavigationbar,_localizederror,_self,_self,_protocol,_extension,_typecasting,_optional,_dismiss/index.html b/docs/documentation/arinote/211025_uinavigationbar,_localizederror,_self,_self,_protocol,_extension,_typecasting,_optional,_dismiss/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211025_uinavigationbar,_localizederror,_self,_self,_protocol,_extension,_typecasting,_optional,_dismiss/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211026_device_orientation,_navigationviewcontroller,_madal,_gesture,_naming/index.html b/docs/documentation/arinote/211026_device_orientation,_navigationviewcontroller,_madal,_gesture,_naming/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211026_device_orientation,_navigationviewcontroller,_madal,_gesture,_naming/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211028_uistepper,_auto_layout,_prepare,_extension,_customstringconvertible,_nsbundle,_uibutton/index.html b/docs/documentation/arinote/211028_uistepper,_auto_layout,_prepare,_extension,_customstringconvertible,_nsbundle,_uibutton/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211028_uistepper,_auto_layout,_prepare,_extension,_customstringconvertible,_nsbundle,_uibutton/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211029_viewcontroller,_life_cycle,_notificationcenter,_deinit,_bundle,_preview/index.html b/docs/documentation/arinote/211029_viewcontroller,_life_cycle,_notificationcenter,_deinit,_bundle,_preview/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211029_viewcontroller,_life_cycle,_notificationcenter,_deinit,_bundle,_preview/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211030_auto_layout/index.html b/docs/documentation/arinote/211030_auto_layout/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211030_auto_layout/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211101_kvo,_property_observer,_prepare,_loadviewifneeded,_nsobject,_uml,_auto_layout/index.html b/docs/documentation/arinote/211101_kvo,_property_observer,_prepare,_loadviewifneeded,_nsobject,_uml,_auto_layout/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211101_kvo,_property_observer,_prepare,_loadviewifneeded,_nsobject,_uml,_auto_layout/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211102_loadviewifneeded,_adjustsfontforcontentsizecategory,_adjustsfontsizetofitwidth,_uibutton,_navigationviewcontroller,_removearrangedsubview/index.html b/docs/documentation/arinote/211102_loadviewifneeded,_adjustsfontforcontentsizecategory,_adjustsfontsizetofitwidth,_uibutton,_navigationviewcontroller,_removearrangedsubview/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211102_loadviewifneeded,_adjustsfontforcontentsizecategory,_adjustsfontsizetofitwidth,_uibutton,_navigationviewcontroller,_removearrangedsubview/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211103_tdd,_unit_test,_tableview/index.html b/docs/documentation/arinote/211103_tdd,_unit_test,_tableview/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211103_tdd,_unit_test,_tableview/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211104_tdd,_unit_test,_@discardableresult,_cell_style,_droplast(),_removelast(),_poplast()/index.html b/docs/documentation/arinote/211104_tdd,_unit_test,_@discardableresult,_cell_style,_droplast(),_removelast(),_poplast()/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211104_tdd,_unit_test,_@discardableresult,_cell_style,_droplast(),_removelast(),_poplast()/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211105_outlet_collection,_extension,_---_--_--,_---_--/index.html b/docs/documentation/arinote/211105_outlet_collection,_extension,_---_--_--,_---_--/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211105_outlet_collection,_extension,_---_--_--,_---_--/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211108_delegate,_weak,_arc,_element,_queue,_stack,_linkelist/index.html b/docs/documentation/arinote/211108_delegate,_weak,_arc,_element,_queue,_stack,_linkelist/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211108_delegate,_weak,_arc,_element,_queue,_stack,_linkelist/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211109_removeall,_attribute,_commit,_numeric,_floatingpoint,_nan,_where,_sourcetree/index.html b/docs/documentation/arinote/211109_removeall,_attribute,_commit,_numeric,_floatingpoint,_nan,_where,_sourcetree/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211109_removeall,_attribute,_commit,_numeric,_floatingpoint,_nan,_where,_sourcetree/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211111_capture_list,_solid,_protocol,_split,_uml/index.html b/docs/documentation/arinote/211111_capture_list,_solid,_protocol,_split,_uml/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211111_capture_list,_solid,_protocol,_split,_uml/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211112_delegation,_numberformatter,_finding_and_refactoring_code,_snapkit/index.html b/docs/documentation/arinote/211112_delegation,_numberformatter,_finding_and_refactoring_code,_snapkit/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211112_delegation,_numberformatter,_finding_and_refactoring_code,_snapkit/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211115_lldb,_complie,_build,_link,_split,_components,_import,_get/index.html b/docs/documentation/arinote/211115_lldb,_complie,_build,_link,_split,_components,_import,_get/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211115_lldb,_complie,_build,_link,_split,_components,_import,_get/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211116_scroll_view,_stack_view,_setcontentoffset,_layoutifneeded,_setneedslayout/index.html b/docs/documentation/arinote/211116_scroll_view,_stack_view,_setcontentoffset,_layoutifneeded,_setneedslayout/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211116_scroll_view,_stack_view,_setcontentoffset,_layoutifneeded,_setneedslayout/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211118_functional_programming,_higher-order_function,_main_run_loop,_update_cycle,_uiview,_attribute/index.html b/docs/documentation/arinote/211118_functional_programming,_higher-order_function,_main_run_loop,_update_cycle,_uiview,_attribute/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211118_functional_programming,_higher-order_function,_main_run_loop,_update_cycle,_uiview,_attribute/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211119_viewdidload,_localizederror,_scrollview_bar_hide,_addsubview,_ibinspectable,_cornerradius,_viewwilllayoutsubviews,_private_extension/index.html b/docs/documentation/arinote/211119_viewdidload,_localizederror,_scrollview_bar_hide,_addsubview,_ibinspectable,_cornerradius,_viewwilllayoutsubviews,_private_extension/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211119_viewdidload,_localizederror,_scrollview_bar_hide,_addsubview,_ibinspectable,_cornerradius,_viewwilllayoutsubviews,_private_extension/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211121_oslog/index.html b/docs/documentation/arinote/211121_oslog/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211121_oslog/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211122_arc,_oslog,_debugprint,_assert,_initializer,_scrollrecttovisible,_cgrect/index.html b/docs/documentation/arinote/211122_arc,_oslog,_debugprint,_assert,_initializer,_scrollrecttovisible,_cgrect/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211122_arc,_oslog,_debugprint,_assert,_initializer,_scrollrecttovisible,_cgrect/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211123_app_life_cycle,_init(frame:),_init(coder:),_frame,_bounds,_iboutlet_didset,_updateviewconstraints/index.html b/docs/documentation/arinote/211123_app_life_cycle,_init(frame:),_init(coder:),_frame,_bounds,_iboutlet_didset,_updateviewconstraints/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211123_app_life_cycle,_init(frame:),_init(coder:),_frame,_bounds,_iboutlet_didset,_updateviewconstraints/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211125_auto_layout,_view_life_cycle,_staticstring,_fork--_repository_-----/index.html b/docs/documentation/arinote/211125_auto_layout,_view_life_cycle,_staticstring,_fork--_repository_-----/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211125_auto_layout,_view_life_cycle,_staticstring,_fork--_repository_-----/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211126_app_life_cycle,_cfgetretaincount,_readme/index.html b/docs/documentation/arinote/211126_app_life_cycle,_cfgetretaincount,_readme/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211126_app_life_cycle,_cfgetretaincount,_readme/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211203_tableview,_-----/index.html b/docs/documentation/arinote/211203_tableview,_-----/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211203_tableview,_-----/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211206_tableview_----,_json,_api/index.html b/docs/documentation/arinote/211206_tableview_----,_json,_api/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211206_tableview_----,_json,_api/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211207_nsdataasset,_----,_contents.json,_framework,_library,_api/index.html b/docs/documentation/arinote/211207_nsdataasset,_----,_contents.json,_framework,_library,_api/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211207_nsdataasset,_----,_contents.json,_framework,_library,_api/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211208_navigation_bar,_uiimageview,_nsmutableattributedstring,_addattribute/index.html b/docs/documentation/arinote/211208_navigation_bar,_uiimageview,_nsmutableattributedstring,_addattribute/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211208_navigation_bar,_uiimageview,_nsmutableattributedstring,_addattribute/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211209_-----,_tableview,_xib,_celldidselect,_select-_------,_indexpathforselectedrow/index.html b/docs/documentation/arinote/211209_-----,_tableview,_xib,_celldidselect,_select-_------,_indexpathforselectedrow/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211209_-----,_tableview,_xib,_celldidselect,_select-_------,_indexpathforselectedrow/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211210_loadviewifneeded,_storyboard_reference/index.html b/docs/documentation/arinote/211210_loadviewifneeded,_storyboard_reference/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211210_loadviewifneeded,_storyboard_reference/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211213_accessibility,_dynamic_type,_voice_over,_fatalerror,_---_-----,_vim/index.html b/docs/documentation/arinote/211213_accessibility,_dynamic_type,_voice_over,_fatalerror,_---_-----,_vim/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211213_accessibility,_dynamic_type,_voice_over,_fatalerror,_---_-----,_vim/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211214_static,_factory_pattern,_visibleviewcontroller/index.html b/docs/documentation/arinote/211214_static,_factory_pattern,_visibleviewcontroller/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211214_static,_factory_pattern,_visibleviewcontroller/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211215_-----_------/index.html b/docs/documentation/arinote/211215_-----_------/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211215_-----_------/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211216_sync,_async,_parallel,_concurrency,_gcd/index.html b/docs/documentation/arinote/211216_sync,_async,_parallel,_concurrency,_gcd/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211216_sync,_async,_parallel,_concurrency,_gcd/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211217_gcd/index.html b/docs/documentation/arinote/211217_gcd/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211217_gcd/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211220_---_----,_----/index.html b/docs/documentation/arinote/211220_---_----,_----/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211220_---_----,_----/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211221_collectionview,_unit_test/index.html b/docs/documentation/arinote/211221_collectionview,_unit_test/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211221_collectionview,_unit_test/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211222_uicollectionviewflowlayout,_pop/index.html b/docs/documentation/arinote/211222_uicollectionviewflowlayout,_pop/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211222_uicollectionviewflowlayout,_pop/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211223_pop,_delegate/index.html b/docs/documentation/arinote/211223_pop,_delegate/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211223_pop,_delegate/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211224_escapingclosure,_autoclosure,_weak,_wildcard,_naming/index.html b/docs/documentation/arinote/211224_escapingclosure,_autoclosure,_weak,_wildcard,_naming/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211224_escapingclosure,_autoclosure,_weak,_wildcard,_naming/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211226_associated_type,_responder_chain,_touch_event,_run_loop/index.html b/docs/documentation/arinote/211226_associated_type,_responder_chain,_touch_event,_run_loop/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211226_associated_type,_responder_chain,_touch_event,_run_loop/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211227_responder_chain,_touch_event,_dispatchsemaphore/index.html b/docs/documentation/arinote/211227_responder_chain,_touch_event,_dispatchsemaphore/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211227_responder_chain,_touch_event,_dispatchsemaphore/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211228_race_condition,_dispatchsemaphore,_http/index.html b/docs/documentation/arinote/211228_race_condition,_dispatchsemaphore,_http/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211228_race_condition,_dispatchsemaphore,_http/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211229_tcp_ip/index.html b/docs/documentation/arinote/211229_tcp_ip/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211229_tcp_ip/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211230_http,_udid,_uuid/index.html b/docs/documentation/arinote/211230_http,_udid,_uuid/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211230_http,_udid,_uuid/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/211231_inout,_async,_await,_thread_sanitizer/index.html b/docs/documentation/arinote/211231_inout,_async,_await,_thread_sanitizer/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/211231_inout,_async,_await,_thread_sanitizer/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220103_uicollectionview,_http,_urlsession/index.html b/docs/documentation/arinote/220103_uicollectionview,_http,_urlsession/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220103_uicollectionview,_http,_urlsession/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220104_multipart,_form-data,_aync_method_test,_---_--,_test_doubles/index.html b/docs/documentation/arinote/220104_multipart,_form-data,_aync_method_test,_---_--,_test_doubles/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220104_multipart,_form-data,_aync_method_test,_---_--,_test_doubles/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220105_urlprotocolmock,_wwdc2018,_urlsession,_networktest/index.html b/docs/documentation/arinote/220105_urlprotocolmock,_wwdc2018,_urlsession,_networktest/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220105_urlprotocolmock,_wwdc2018,_urlsession,_networktest/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220106_uisegmentedcontrol,_----pr1---/index.html b/docs/documentation/arinote/220106_uisegmentedcontrol,_----pr1---/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220106_uisegmentedcontrol,_----pr1---/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220107_tableview,_collectionviewlistcell,_contentoffset,_contentinset/index.html b/docs/documentation/arinote/220107_tableview,_collectionviewlistcell,_contentoffset,_contentinset/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220107_tableview,_collectionviewlistcell,_contentoffset,_contentinset/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220110_filemanager,_ios_file_system,_multipart_form-data/index.html b/docs/documentation/arinote/220110_filemanager,_ios_file_system,_multipart_form-data/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220110_filemanager,_ios_file_system,_multipart_form-data/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220110_nscache/index.html b/docs/documentation/arinote/220110_nscache/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220110_nscache/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220113_cache,_pagination/index.html b/docs/documentation/arinote/220113_cache,_pagination/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220113_cache,_pagination/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220114_implementing_modern_collection_views_custom/index.html b/docs/documentation/arinote/220114_implementing_modern_collection_views_custom/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220114_implementing_modern_collection_views_custom/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220117_-----_--_--,_uiimagepickercontroller/index.html b/docs/documentation/arinote/220117_-----_--_--,_uiimagepickercontroller/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220117_-----_--_--,_uiimagepickercontroller/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220118_uitextfield,_keyboard,_uitextview,_scrollview,_touchesbegan,_recognizer,_uiimage/index.html b/docs/documentation/arinote/220118_uitextfield,_keyboard,_uitextview,_scrollview,_touchesbegan,_recognizer,_uiimage/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220118_uitextfield,_keyboard,_uitextview,_scrollview,_touchesbegan,_recognizer,_uiimage/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220120_uialertcontroller,_keychain/index.html b/docs/documentation/arinote/220120_uialertcontroller,_keychain/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220120_uialertcontroller,_keychain/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220121_selectedbackgroundview,_uirefreshcontrol,_---,_paging/index.html b/docs/documentation/arinote/220121_selectedbackgroundview,_uirefreshcontrol,_---,_paging/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220121_selectedbackgroundview,_uirefreshcontrol,_---,_paging/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220124_coredata,_dynamic_type,_uifontmetrics,_setcustomspacing,_viewforzooming,_------,_sql/index.html b/docs/documentation/arinote/220124_coredata,_dynamic_type,_uifontmetrics,_setcustomspacing,_viewforzooming,_------,_sql/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220124_coredata,_dynamic_type,_uifontmetrics,_setcustomspacing,_viewforzooming,_------,_sql/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220125_uitapgesturerecognizer,_uicollectionview/index.html b/docs/documentation/arinote/220125_uitapgesturerecognizer,_uicollectionview/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220125_uitapgesturerecognizer,_uicollectionview/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220127_uitextfield,_animation,_mvvm,_observable,_uinavigationbarappearance,_appearance/index.html b/docs/documentation/arinote/220127_uitextfield,_animation,_mvvm,_observable,_uinavigationbarappearance,_appearance/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220127_uitextfield,_animation,_mvvm,_observable,_uinavigationbarappearance,_appearance/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220128_coredata,_nspredicate,_relationship/index.html b/docs/documentation/arinote/220128_coredata,_nspredicate,_relationship/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220128_coredata,_nspredicate,_relationship/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220207_view_drawing_cycle,_uisplitviewcontroller--/index.html b/docs/documentation/arinote/220207_view_drawing_cycle,_uisplitviewcontroller--/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220207_view_drawing_cycle,_uisplitviewcontroller--/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220208_reloadrows,_cancelstouchesinview,_translatesautoresizingmaskintoconstraints,_array_safe_access,_barbuttonsystemitem,_uisplitviewcontrollerdelegate/index.html b/docs/documentation/arinote/220208_reloadrows,_cancelstouchesinview,_translatesautoresizingmaskintoconstraints,_array_safe_access,_barbuttonsystemitem,_uisplitviewcontrollerdelegate/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220208_reloadrows,_cancelstouchesinview,_translatesautoresizingmaskintoconstraints,_array_safe_access,_barbuttonsystemitem,_uisplitviewcontrollerdelegate/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220210_core_graphics,_codegen,_returnsobjectsasfaults,_nsfetchrequestresult,_array_item_to_new_position/index.html b/docs/documentation/arinote/220210_core_graphics,_codegen,_returnsobjectsasfaults,_nsfetchrequestresult,_array_item_to_new_position/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220210_core_graphics,_codegen,_returnsobjectsasfaults,_nsfetchrequestresult,_array_item_to_new_position/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220211_uicontextualaction,_uialertaction,_uiactivityviewcontroller,_setselected,_selectedbackgroundview,_actionsheet,_popoverpresentationcontroller,_viewwilltransition/index.html b/docs/documentation/arinote/220211_uicontextualaction,_uialertaction,_uiactivityviewcontroller,_setselected,_selectedbackgroundview,_actionsheet,_popoverpresentationcontroller,_viewwilltransition/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220211_uicontextualaction,_uialertaction,_uiactivityviewcontroller,_setselected,_selectedbackgroundview,_actionsheet,_popoverpresentationcontroller,_viewwilltransition/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220214_core_animation,_cabasicanimation,_append,_escaping,_uitableview-crash,_uitextviewdelegate,_typingattributes/index.html b/docs/documentation/arinote/220214_core_animation,_cabasicanimation,_append,_escaping,_uitableview-crash,_uitextviewdelegate,_typingattributes/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220214_core_animation,_cabasicanimation,_append,_escaping,_uitableview-crash,_uitextviewdelegate,_typingattributes/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220215_dropbox,_weak_self,_escaping,_delayed_deallocation,_performbatchupdate,_setediting/index.html b/docs/documentation/arinote/220215_dropbox,_weak_self,_escaping,_delayed_deallocation,_performbatchupdate,_setediting/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220215_dropbox,_weak_self,_escaping,_delayed_deallocation,_performbatchupdate,_setediting/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220217_dropbox,_rxswift,_spm,_dispatchgroup/index.html b/docs/documentation/arinote/220217_dropbox,_rxswift,_spm,_dispatchgroup/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220217_dropbox,_rxswift,_spm,_dispatchgroup/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220218_rxswift,_rxcocoa,_rxrelay,_mvvm/index.html b/docs/documentation/arinote/220218_rxswift,_rxcocoa,_rxrelay,_mvvm/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220218_rxswift,_rxcocoa,_rxrelay,_mvvm/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220221_localization,_--_----,_swiftui_tutorial/index.html b/docs/documentation/arinote/220221_localization,_--_----,_swiftui_tutorial/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220221_localization,_--_----,_swiftui_tutorial/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220222_uisearchcontroller/index.html b/docs/documentation/arinote/220222_uisearchcontroller/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220222_uisearchcontroller/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220224_swiftui,_property_wrapper,_divider,_list,_geometryreader/index.html b/docs/documentation/arinote/220224_swiftui,_property_wrapper,_divider,_list,_geometryreader/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220224_swiftui,_property_wrapper,_divider,_list,_geometryreader/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220225_greedy,_wholenumbervalue,_enumerated,_reduce/index.html b/docs/documentation/arinote/220225_greedy,_wholenumbervalue,_enumerated,_reduce/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220225_greedy,_wholenumbervalue,_enumerated,_reduce/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220228_usernotifications,_appdelegate/index.html b/docs/documentation/arinote/220228_usernotifications,_appdelegate/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220228_usernotifications,_appdelegate/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220301_mvvm,_----_--/index.html b/docs/documentation/arinote/220301_mvvm,_----_--/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220301_mvvm,_----_--/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220303_clean_architecture_mvvm,_async,_await/index.html b/docs/documentation/arinote/220303_clean_architecture_mvvm,_async,_await/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220303_clean_architecture_mvvm,_async,_await/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220304_rxswift,_observable,_subject/index.html b/docs/documentation/arinote/220304_rxswift,_observable,_subject/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220304_rxswift,_observable,_subject/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220307_high_performance_auto_layout,_rxswift,_single,_dto/index.html b/docs/documentation/arinote/220307_high_performance_auto_layout,_rxswift,_single,_dto/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220307_high_performance_auto_layout,_rxswift,_single,_dto/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220310_oauth,_social_login,_tls,_---,_----,_---,_---/index.html b/docs/documentation/arinote/220310_oauth,_social_login,_tls,_---,_----,_---,_---/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220310_oauth,_social_login,_tls,_---,_----,_---,_---/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220314_mvvm,_rx,_changed,_coordinator,_observable/index.html b/docs/documentation/arinote/220314_mvvm,_rx,_changed,_coordinator,_observable/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220314_mvvm,_rx,_changed,_coordinator,_observable/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220315_memory_leak,_rxswift,_withunretained,_widget/index.html b/docs/documentation/arinote/220315_memory_leak,_rxswift,_withunretained,_widget/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220315_memory_leak,_rxswift,_withunretained,_widget/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220317_widget,_timeline,_decorator_pattern/index.html b/docs/documentation/arinote/220317_widget,_timeline,_decorator_pattern/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220317_widget,_timeline,_decorator_pattern/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220318_uistoryboard,_spell_checking_in_xcode,_quick_help/index.html b/docs/documentation/arinote/220318_uistoryboard,_spell_checking_in_xcode,_quick_help/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220318_uistoryboard,_spell_checking_in_xcode,_quick_help/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220321_app_distribution,_hashable/index.html b/docs/documentation/arinote/220321_app_distribution,_hashable/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220321_app_distribution,_hashable/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220322_flyweight,_facade/index.html b/docs/documentation/arinote/220322_flyweight,_facade/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220322_flyweight,_facade/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220324_-----_----/index.html b/docs/documentation/arinote/220324_-----_----/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220324_-----_----/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220520_urlsession,_nscache,_-_---/index.html b/docs/documentation/arinote/220520_urlsession,_nscache,_-_---/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220520_urlsession,_nscache,_-_---/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220523_api,_rest_api,_array,_set,_tuple,_library,_map/index.html b/docs/documentation/arinote/220523_api,_rest_api,_array,_set,_tuple,_library,_map/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220523_api,_rest_api,_array,_set,_tuple,_library,_map/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220613_github_actions,_ci,_cd,_---/index.html b/docs/documentation/arinote/220613_github_actions,_ci,_cd,_---/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220613_github_actions,_ci,_cd,_---/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220711_arc,_dismiss,_popviewcontroller,_memory_leak/index.html b/docs/documentation/arinote/220711_arc,_dismiss,_popviewcontroller,_memory_leak/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220711_arc,_dismiss,_popviewcontroller,_memory_leak/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220813_rxswift,_almofire,_urlprotocol,_unit_test/index.html b/docs/documentation/arinote/220813_rxswift,_almofire,_urlprotocol,_unit_test/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220813_rxswift,_almofire,_urlprotocol,_unit_test/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/220923_urlsession,_cachepolicy/index.html b/docs/documentation/arinote/220923_urlsession,_cachepolicy/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/220923_urlsession,_cachepolicy/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/230710_addsubview_referencecount/index.html b/docs/documentation/arinote/230710_addsubview_referencecount/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/230710_addsubview_referencecount/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/230904_keyboardlayoutguide/index.html b/docs/documentation/arinote/230904_keyboardlayoutguide/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/230904_keyboardlayoutguide/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/230911_opennotificationsettingsurlstring/index.html b/docs/documentation/arinote/230911_opennotificationsettingsurlstring/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/230911_opennotificationsettingsurlstring/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/230915_uiimage_withtintcolor_renderingmode/index.html b/docs/documentation/arinote/230915_uiimage_withtintcolor_renderingmode/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/230915_uiimage_withtintcolor_renderingmode/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/231201_avplayer,_waitingtoplayatspecifiedrate,_waitingtoplayatspecifiedrate/index.html b/docs/documentation/arinote/231201_avplayer,_waitingtoplayatspecifiedrate,_waitingtoplayatspecifiedrate/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/231201_avplayer,_waitingtoplayatspecifiedrate,_waitingtoplayatspecifiedrate/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/240104_userdefaults,_xcode_find_and_replace_regular_expression/index.html b/docs/documentation/arinote/240104_userdefaults,_xcode_find_and_replace_regular_expression/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/240104_userdefaults,_xcode_find_and_replace_regular_expression/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/240227_photokit,_phpickerviewcontroller,_phpickerconfiguration,_phassetcollection/index.html b/docs/documentation/arinote/240227_photokit,_phpickerviewcontroller,_phpickerconfiguration,_phassetcollection/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/240227_photokit,_phpickerviewcontroller,_phpickerconfiguration,_phassetcollection/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/architecture/index.html b/docs/documentation/arinote/architecture/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/architecture/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/cs/index.html b/docs/documentation/arinote/cs/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/cs/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/git/index.html b/docs/documentation/arinote/git/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/git/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/index.html b/docs/documentation/arinote/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/rx/index.html b/docs/documentation/arinote/rx/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/rx/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/swift/index.html b/docs/documentation/arinote/swift/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/swift/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/swiftui/index.html b/docs/documentation/arinote/swiftui/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/swiftui/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/uikit/index.html b/docs/documentation/arinote/uikit/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/uikit/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/documentation/arinote/xcode/index.html b/docs/documentation/arinote/xcode/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/documentation/arinote/xcode/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/favicon.ico b/docs/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..5231da6dc99b41b8c9b720113cc4991529eb215e GIT binary patch literal 15406 zcmeI0eXLhy9l(EZk(V;zI<*U%aPfsugKmE)10f(cwHIH|L9U{ah=EJiM6*Bqr=-%B zThmgUiaYBb7lU3O68JDrPAE|?)})tN@Xd!mX^#urc&8-W~DL~ zA3}(DSI?zd>5t=7SPZK&9IMk-CKECK0-k`U;cFR=)oIfwPriimM=%tA34>uqhGTWw z^!XyhV$KJkAAAL7f)e5#tD6n_=o|X*H*J9*!w=xkaDO&l!&TI))8<%TW5QhB$+!uI zgTK|W+?WyW{SEqf|1Ajfd@f__<&V_U0+&D*$rJDKckKge;Kf=4$g1yzV zevDUKll89O?gaefnyh#KgdEOCSXb^j@flUKe{ri#JZG9=8hAeSq6{S0`EUiecg3+f zZG9m_NO~Q(PhNvAI0l^=j@4OB}1yrjNeH7;BDt zWHdpouphn+qu>SD3}=Ay(+tPz{s`+qUt^3l$7rZUrr_*cu7eQkS-Ka@9VD)=PTM~C zrZ)2K)rVdOADP-OM$Xw7xcdj5kIxhq;`Ko_erfdtCwz5ab>P+hI^p zWY@g<`siy+5ObN+++jY0EnHnEt*{XW2W2+i&GoiO7lwKDwcQT8Anwv1zHlea=$Fm1)sI(CEUl?<>-x#b&fv;*0N?0-z}1g&&lU_V`|9mi=Myc zxnOMGv-X*JZYk~$*X38eNyO)Q1TIeW;kK-PSOZPLIl9{09^9bKRvHMhR+!J{={mSZ}p2hmSQ_*J4%V8!QhMz&m7p^a$ z^Ea%@vFY>ojNl(%&1dX-i1m)OS<~8~eqN2^sc*ib`r1PMD;PT$d2{UC)jR*@Gu9lb z#PdG}W6}C^o%kE#cV3(;%xkO*<7(I6Kmv;PpL@ss=bx5&QdZv>WApZ(=SkRq=Jo9H zOi1N;&!1OGn0Nm8w?sI9tm#?k8t_@A=jET!Ez8mCw>Be<@qQT?o7${ltr-yHM8ALR z<0f#uP6N-~fbUS(;DksQY(6ueUAPZdeT_-qzthm=y?;@6Kc`l%ek0_4e=O1|k)rjd z?=Bb=`SPepAKywB^;;M_*%tm}?1`+Ztu!H^3Y4AhQ*Fos_PR7-If9s>KVyrpLRc&otyT#a6*bmJiTl_eC4Te}Daj`>Vh+L>zB}qp%5jZldfi5@( zof(eRXh;0!C&y zR;NuLeNP6lk6|zgJOl58r@(i`R&ei$V|Ci}(bpJbPta6h`nD1o*f5aCHPx{SnpBrx!(-~ zAeN&3_0e}R82bo>`C!;xVZh`4g&u_3-dw&O(faiBSzn|gW=k0oKhE&S>*S8sr zHOG|@#2VJJrnUVI;TbWkN}BNU`8hx484R&}n44$8Ir9G$ozGNy>fe|xlb-rFW<0o;&1KG1tZfhWVo&x~Ca(Fp;C|c= zE#UKv#d*66Le5iSV>GU&e`A+sxy)(qSgdUi_F_-=W{*WO3WH~`z1;#sau|-z=iLcc z=h#EcMc8&`I1c*sY7TQf1LjWc-hUnJ$#vvAu1H+V`n?6dS02h?1-gCU-ddhx4>7B- zc}9xkpii&nFxOr%cWU?EUj7Z9tBW8{Tx;zR-tS$E^U!{*3O{4@le|2!uG*aDYKL@B zy3Q76b)Mro3$86dyU%-Zfjz`6!0w!i@ses#^MQCh%`y{q#M6>ODD!(GJ)J6Em&9%fBZMfdA(;5Q^kn4DO?Ka7K|+rmv;{ zGl^Lb=C;Qo8HC|iV6XOU@9ORY*WWjwOeSM=udRfSlsRInrGIm+0&|CXWtc0sp|#f^ zg1w&xt3llqD3j|jc7bQwhsqqW)zZJY9tQh&ua?PFjP~#(*t_3K8(}|`t$obh?>}S1 zZ&z0&we%m>fVt-sCCv1$FV7i!w)YXR6?`XordYR#d$IYP?uU<+_!)m0yqe*-D5rXJ zJpktZK9u>|qrKX*y^n-#VC+AjqlAB9^Bv-KXNg}2HhcHIDvmo!Fqh9iPtP`M^u_sUPIK3Kehwn0 z+V|I`_TWA^yc@2xy)J3_rufp#orIy zcK=`MZmv7P9*g8u3~jI-nxRZQ!#gq@mq{~5bDF#C`_1n!dyK!|xb6NwuvV^b!ff!F z6p7!sufrup4w=4|{>^31Uzg;xCwsHU4KNgfXj=$f@I1@{_fZ~e(Radk3+y4Lp1)tr z9pclgJv|5Zcq^oH+=Q+jdA^W=7vMjXm z#SG{E8E_3u{q3TPIJdL$M#?^_GE7(p-37q%mu$sJr7zSU1tN( z-38`*3{HVOsb>EgYYuan)7-IG`;%ZV_GEAUp-dVvhMcPz=dHEr==VSyjDtL>rvLE- z?f`R5gH)_-5B|PK!QQG7<9t>JVF~ygWATiB7PPx((s@JG{SDv2Rs1cf7;^_40&|%& z7Hj%9nfBkoN1+yR&29nrmcPOHcr9JY{oC*d7zL^HH2=*6YzA|fD~L6$Wz9~ohYO)r zaW8xtjCY<_LY!wH_dZi?cZ2g8OIiQ=E(Yh;9OepQjg??tYr9tMr5-WPe6A(Wt{35Y z2=y*t{1Ujgu7UXZ6!ou z4g7WtbCuV>w#k{VF~*vs1ucb-aRa=P;aJ^X(8oAqjCE`- zbDFz1)(dHR6xYY@7ee^YE1DL~HCqmqduHQ*85BwYUDcA^G yVOxe{bx(pm`c4GDf#W^T?Fke6@Nd!6;4Byct_N|fPMba_OV70Of&4$uz<&VCpkVL- literal 0 HcmV?d00001 diff --git a/docs/favicon.svg b/docs/favicon.svg new file mode 100644 index 0000000..c54c53f --- /dev/null +++ b/docs/favicon.svg @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/docs/img/added-icon.832a5d2c.svg b/docs/img/added-icon.832a5d2c.svg new file mode 100644 index 0000000..6bb6d89 --- /dev/null +++ b/docs/img/added-icon.832a5d2c.svg @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/docs/img/deprecated-icon.7bf1740a.svg b/docs/img/deprecated-icon.7bf1740a.svg new file mode 100644 index 0000000..a0f8008 --- /dev/null +++ b/docs/img/deprecated-icon.7bf1740a.svg @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/docs/img/modified-icon.efb2697d.svg b/docs/img/modified-icon.efb2697d.svg new file mode 100644 index 0000000..3e0bd6f --- /dev/null +++ b/docs/img/modified-icon.efb2697d.svg @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..ab91f9f --- /dev/null +++ b/docs/index.html @@ -0,0 +1 @@ +Documentation
\ No newline at end of file diff --git a/docs/index/index.json b/docs/index/index.json new file mode 100644 index 0000000..bb4be3f --- /dev/null +++ b/docs/index/index.json @@ -0,0 +1 @@ +{"includedArchiveIdentifiers":["AriNote"],"interfaceLanguages":{"swift":[{"children":[{"children":[{"path":"\/documentation\/arinote\/220523_api,_rest_api,_array,_set,_tuple,_library,_map","title":"220523 API, REST API, Array, Set, Tuple, Library, map","type":"article"},{"path":"\/documentation\/arinote\/220310_oauth,_social_login,_tls,_---,_----,_---,_---","title":"220310 OAuth, Social Login, TLS, 대칭키, 비대칭키, 암호화, 복호화","type":"article"},{"path":"\/documentation\/arinote\/220225_greedy,_wholenumbervalue,_enumerated,_reduce","title":"220225 Greedy, wholeNumberValue, enumerated, reduce","type":"article"},{"path":"\/documentation\/arinote\/220113_cache,_pagination","title":"220113 Cache, pagination","type":"article"},{"path":"\/documentation\/arinote\/220110_nscache","title":"220110 NSCache","type":"article"},{"path":"\/documentation\/arinote\/220104_multipart,_form-data,_aync_method_test,_---_--,_test_doubles","title":"220104 multipart, form-data, aync method test, 의존성 주입, Test Doubles","type":"article"},{"path":"\/documentation\/arinote\/211230_http,_udid,_uuid","title":"211230 HTTP, UDID, UUID","type":"article"},{"path":"\/documentation\/arinote\/211229_tcp_ip","title":"211229 TCP\/IP","type":"article"},{"path":"\/documentation\/arinote\/211228_race_condition,_dispatchsemaphore,_http","title":"211228 Race Condition, DispatchSemaphore, HTTP","type":"article"},{"path":"\/documentation\/arinote\/211223_pop,_delegate","title":"211223 POP, delegate","type":"article"},{"path":"\/documentation\/arinote\/211217_gcd","title":"211217 GCD","type":"article"},{"path":"\/documentation\/arinote\/211216_sync,_async,_parallel,_concurrency,_gcd","title":"211216 sync, async, Parallel, Concurrency, GCD","type":"article"},{"path":"\/documentation\/arinote\/211215_-----_------","title":"211215 운영체제와 데이터베이스.md","type":"article"},{"path":"\/documentation\/arinote\/211207_nsdataasset,_----,_contents.json,_framework,_library,_api","title":"211207 NSDataAsset, 오답노트, Contents.JSON, Framework, Library, API","type":"article"},{"path":"\/documentation\/arinote\/211018_let,_var,_tool_bar,_design_patterns,_architectures,_mvc,_dictionary,_downcasting,_function_types_as_parameter_types","title":"211018 let, var, tool bar, Design Patterns, Architectures, MVC, Dictionary, Downcasting, Function Types as Parameter Types","type":"article"},{"path":"\/documentation\/arinote\/210812_---_--","title":"210812 메모리 구조","type":"article"},{"path":"\/documentation\/arinote\/210804_-------","title":"210804 동적프로그래밍","type":"article"},{"path":"\/documentation\/arinote\/210802_---_--","title":"210802 정수와 실수","type":"article"}],"path":"\/documentation\/arinote\/cs","title":"CS","type":"symbol"},{"children":[{"path":"\/documentation\/arinote\/220613_github_actions,_ci,_cd,_---","title":"220613 Github Actions, CI, CD, 자동화","type":"article"},{"path":"\/documentation\/arinote\/211005_git,-_-----_--","title":"211004 사전과제, 짝프로그래밍, Git기초","type":"article"},{"path":"\/documentation\/arinote\/211004_---------,_--------------,_git----","title":"211004 사전과제, 짝프로그래밍, Git기초","type":"article"}],"path":"\/documentation\/arinote\/git","title":"Git","type":"symbol"},{"children":[{"path":"\/documentation\/arinote\/220813_rxswift,_almofire,_urlprotocol,_unit_test","title":"220813 RxSwift, Almofire, URLProtocol, Unit Test","type":"article"},{"path":"\/documentation\/arinote\/220314_mvvm,_rx,_changed,_coordinator,_observable","title":"220314 MVVM, Rx, changed, coordinator, observable","type":"article"},{"path":"\/documentation\/arinote\/220304_rxswift,_observable,_subject","title":"220304 RxSwift, Observable, Subject","type":"article"},{"path":"\/documentation\/arinote\/220218_rxswift,_rxcocoa,_rxrelay,_mvvm","title":"220218 RxSwift, RxCocoa, RxRelay, MVVM","type":"article"},{"path":"\/documentation\/arinote\/220217_dropbox,_rxswift,_spm,_dispatchgroup","title":"220217 Dropbox, RxSwift, SPM, DispatchGroup","type":"article"}],"path":"\/documentation\/arinote\/rx","title":"RxSwift","type":"symbol"},{"children":[{"path":"\/documentation\/arinote\/220923_urlsession,_cachepolicy","title":"220923 URLSession, CachePolicy","type":"article"},{"path":"\/documentation\/arinote\/220105_urlprotocolmock,_wwdc2018,_urlsession,_networktest","title":"220105 URLProtocolMock, WWDC2018, URLSession, NetworkTest","type":"article"},{"path":"\/documentation\/arinote\/211231_inout,_async,_await,_thread_sanitizer","title":"211231 inout, async, await, Thread Sanitizer","type":"article"},{"path":"\/documentation\/arinote\/211224_escapingclosure,_autoclosure,_weak,_wildcard,_naming","title":"211224 escapingClosure, autoClosure, weak, wildcard, naming","type":"article"},{"path":"\/documentation\/arinote\/211121_oslog","title":"211121 OSLog","type":"article"},{"path":"\/documentation\/arinote\/211112_delegation,_numberformatter,_finding_and_refactoring_code,_snapkit","title":"211112 Delegation, NumberFormatter, Finding and Refactoring Code, SnapKit","type":"article"},{"path":"\/documentation\/arinote\/211111_capture_list,_solid,_protocol,_split,_uml","title":"211111 Capture List, SOLID, protocol, split, UML","type":"article"},{"path":"\/documentation\/arinote\/211109_removeall,_attribute,_commit,_numeric,_floatingpoint,_nan,_where,_sourcetree","title":"211109 removeAll, attribute, commit, Numeric, FloatingPoint, NaN, where, SourceTree","type":"article"},{"path":"\/documentation\/arinote\/211108_delegate,_weak,_arc,_element,_queue,_stack,_linkelist","title":"211108 Delegate, weak, ARC, Element, Queue, Stack, LinkeList","type":"article"},{"path":"\/documentation\/arinote\/211020_kvc,_notification,_notificationcenter","title":"211020 KVC, Notification, NotificationCenter","type":"article"},{"path":"\/documentation\/arinote\/211019_nested_type,_operator,_dictionary,_sequence,_localizederror,_error_handling","title":"211019 Nested Type, operator, Dictionary, Sequence, LocalizedError, Error Handling","type":"article"},{"path":"\/documentation\/arinote\/211013_naming,_if-else,_switch,_----_----,_reset,_reflog","title":"211013 Naming, if-else, switch, 반복문과 재귀함수, reset, reflog","type":"article"},{"path":"\/documentation\/arinote\/211012_----,_properties,_keypath,_kvc,_ios,_hig","title":"211012 프로젝트, Properties, KeyPath, KVC, iOS, HIG","type":"article"},{"path":"\/documentation\/arinote\/211011_condition,_condition-list,_flow_chart,_------------,_ground_rules,_daily_scrum,_----","title":"211011 condition, condition-list, flow chart, 일반화추상화은닉화캡슐화, Ground rules, Daily Scrum, 메소드명","type":"article"},{"path":"\/documentation\/arinote\/211009_optional,_dictionary,_compactmap","title":"211009 Optional, Dictionary, compactMap","type":"article"},{"path":"\/documentation\/arinote\/211008_destructuring,_commit,_branch,_changes_requested,_refactoring,_-----_----_--,_naming,_reduce","title":"211008 Destructuring, commit, branch, Changes requested, refactoring, 전역변수를 잘안쓰는 이유, naming, reduce","type":"article"},{"path":"\/documentation\/arinote\/211007_----,---,kvo,compactmap,----","title":"211007 땅따먹기,싱글톤,KVO,compactMap,프로젝트","type":"article"},{"path":"\/documentation\/arinote\/211006_------,lazy,---,----,----,-----_----_----","title":"211006 첫팀프로젝트,lazy,가독성,성능고려,재사용성,고차함수와 반복문의 성능차이","type":"article"},{"path":"\/documentation\/arinote\/210831_result","title":"210831 Result","type":"article"},{"path":"\/documentation\/arinote\/210827_filemanger","title":"210827 FileManger","type":"article"},{"path":"\/documentation\/arinote\/210820_------,_----_---","title":"210820 이니셜라이저, 궁금했던 키워드","type":"article"},{"path":"\/documentation\/arinote\/210817_----","title":"210817 프로토콜","type":"article"},{"path":"\/documentation\/arinote\/210816_----","title":"210816 접근제어","type":"article"},{"path":"\/documentation\/arinote\/210811_swift---_--_let---_----------","title":"210811 Swift는 왜 let을 선호할까?","type":"article"},{"path":"\/documentation\/arinote\/210809_enum","title":"210809 enum","type":"article"},{"path":"\/documentation\/arinote\/210805_api_design_guidelines","title":"210805 API Design Guidelines","type":"article"},{"path":"\/documentation\/arinote\/210803_for-in_-_foreach","title":"210803 for-in 외 forEach","type":"article"}],"path":"\/documentation\/arinote\/swift","title":"Swift","type":"symbol"},{"children":[{"path":"\/documentation\/arinote\/220317_widget,_timeline,_decorator_pattern","title":"220317 Widget, Timeline, Decorator Pattern","type":"article"},{"path":"\/documentation\/arinote\/220315_memory_leak,_rxswift,_withunretained,_widget","title":"220315 Memory Leak, RxSwift, withUnretained, Widget","type":"article"},{"path":"\/documentation\/arinote\/220224_swiftui,_property_wrapper,_divider,_list,_geometryreader","title":"220224 SwiftUI, Property Wrapper, Divider, List, GeometryReader","type":"article"},{"path":"\/documentation\/arinote\/220221_localization,_--_----,_swiftui_tutorial","title":"220221 Localization, 퀴즈 오답노트, SwiftUI Tutorial","type":"article"}],"path":"\/documentation\/arinote\/swiftui","title":"SwiftUI","type":"symbol"},{"children":[{"path":"\/documentation\/arinote\/240227_photokit,_phpickerviewcontroller,_phpickerconfiguration,_phassetcollection","title":"240227 PhotoKit, PHPickerViewController, PHPickerConfiguration, PHAssetCollection","type":"article"},{"path":"\/documentation\/arinote\/231201_avplayer,_waitingtoplayatspecifiedrate,_waitingtoplayatspecifiedrate","title":"231201 AVPlayer, waitingToPlayAtSpecifiedRate, evaluatingBufferingRate","type":"article"},{"path":"\/documentation\/arinote\/230915_uiimage_withtintcolor_renderingmode","title":"230915 UIImage, withTintColor, renderingMode","type":"article"},{"path":"\/documentation\/arinote\/230911_opennotificationsettingsurlstring","title":"230911 openNotificationSettingsURLString","type":"article"},{"path":"\/documentation\/arinote\/230904_keyboardlayoutguide","title":"230904 keyboardLayoutGuide","type":"article"},{"path":"\/documentation\/arinote\/230710_addsubview_referencecount","title":"230710 addSubview, Reference Count","type":"article"},{"path":"\/documentation\/arinote\/220711_arc,_dismiss,_popviewcontroller,_memory_leak","title":"220711 ARC, dismiss, popViewController, memory leak","type":"article"},{"path":"\/documentation\/arinote\/220520_urlsession,_nscache,_-_---","title":"220520 URLSession, NSCache, 셀 재사용","type":"article"},{"path":"\/documentation\/arinote\/220318_uistoryboard,_spell_checking_in_xcode,_quick_help","title":"220318 UIStoryboard, Spell checking in Xcode, Quick help","type":"article"},{"path":"\/documentation\/arinote\/220307_high_performance_auto_layout,_rxswift,_single,_dto","title":"220307 High Performance Auto Layout, RxSwift, Single, DTO","type":"article"},{"path":"\/documentation\/arinote\/220228_usernotifications,_appdelegate","title":"220228 UserNotifications, AppDelegate","type":"article"},{"path":"\/documentation\/arinote\/220222_uisearchcontroller","title":"220222 UISearchController","type":"article"},{"path":"\/documentation\/arinote\/220215_dropbox,_weak_self,_escaping,_delayed_deallocation,_performbatchupdate,_setediting","title":"220215 Dropbox, weak self, escaping, Delayed Deallocation, performBatchUpdate, setEditing","type":"article"},{"path":"\/documentation\/arinote\/220214_core_animation,_cabasicanimation,_append,_escaping,_uitableview-crash,_uitextviewdelegate,_typingattributes","title":"220214 Core Animation, CABasicAnimation, append, escaping, UITableView-Crash, UITextViewDelegate, typingAttributes","type":"article"},{"path":"\/documentation\/arinote\/220211_uicontextualaction,_uialertaction,_uiactivityviewcontroller,_setselected,_selectedbackgroundview,_actionsheet,_popoverpresentationcontroller,_viewwilltransition","title":"220211 UIContextualAction, UIAlertAction, UIActivityViewController, setSelected, selectedBackgroundView, actionSheet, popoverPresentationController, viewWillTransition","type":"article"},{"path":"\/documentation\/arinote\/220210_core_graphics,_codegen,_returnsobjectsasfaults,_nsfetchrequestresult,_array_item_to_new_position","title":"220210 Core Graphics, codegen, returnsObjectsAsFaults, NSFetchRequestResult, Array item to new position.","type":"article"},{"path":"\/documentation\/arinote\/220208_reloadrows,_cancelstouchesinview,_translatesautoresizingmaskintoconstraints,_array_safe_access,_barbuttonsystemitem,_uisplitviewcontrollerdelegate","title":"220208 reloadRows, cancelsTouchesInView, translatesAutoresizingMaskIntoConstraints, Array Safe Access, barButtonSystemItem, UISplitViewControllerDelegate","type":"article"},{"path":"\/documentation\/arinote\/220207_view_drawing_cycle,_uisplitviewcontroller--","title":"220207 View Drawing Cycle, UISplitViewController이론","type":"article"},{"path":"\/documentation\/arinote\/220128_coredata,_nspredicate,_relationship","title":"220128 CoreData, NSPredicate, Relationship","type":"article"},{"path":"\/documentation\/arinote\/220127_uitextfield,_animation,_mvvm,_observable,_uinavigationbarappearance,_appearance","title":"220127 UITextField, Animation, MVVM, Observable, UINavigationBarAppearance, Appearance","type":"article"},{"path":"\/documentation\/arinote\/220125_uitapgesturerecognizer,_uicollectionview","title":"220125 UITapGestureRecognizer, UICollectionView","type":"article"},{"path":"\/documentation\/arinote\/220124_coredata,_dynamic_type,_uifontmetrics,_setcustomspacing,_viewforzooming,_------,_sql","title":"220124 CoreData, Dynamic Type, UIFontMetrics, setCustomSpacing, viewForZooming, 데이터베이스, SQL","type":"article"},{"path":"\/documentation\/arinote\/220121_selectedbackgroundview,_uirefreshcontrol,_---,_paging","title":"220121 selectedBackgroundView, UIRefreshControl, 피드백, paging","type":"article"},{"path":"\/documentation\/arinote\/220120_uialertcontroller,_keychain","title":"220120 UIAlertController, KeyChain","type":"article"},{"path":"\/documentation\/arinote\/220118_uitextfield,_keyboard,_uitextview,_scrollview,_touchesbegan,_recognizer,_uiimage","title":"220118 UITextField, Keyboard, UITextView, ScrollView, touchesBegan, Recognizer, UIImage","type":"article"},{"path":"\/documentation\/arinote\/220117_-----_--_--,_uiimagepickercontroller","title":"220117 스위프트의 성능 이해, UIImagePickerController","type":"article"},{"path":"\/documentation\/arinote\/220114_implementing_modern_collection_views_custom","title":"220114 Implementing Modern Collection Views Custom","type":"article"},{"path":"\/documentation\/arinote\/220110_filemanager,_ios_file_system,_multipart_form-data","title":"220110 FileManager, iOS File System, Multipart\/form-data","type":"article"},{"path":"\/documentation\/arinote\/220107_tableview,_collectionviewlistcell,_contentoffset,_contentinset","title":"220107 TableView, CollectionViewListCell, ContentOffset, ContentInset","type":"article"},{"path":"\/documentation\/arinote\/220106_uisegmentedcontrol,_----pr1---","title":"220106 UISegmentedControl, 오픈마켓PR1피드백","type":"article"},{"path":"\/documentation\/arinote\/220103_uicollectionview,_http,_urlsession","title":"220103 UICollectionView, HTTP, URLSession","type":"article"},{"path":"\/documentation\/arinote\/211227_responder_chain,_touch_event,_dispatchsemaphore","title":"211227 Responder Chain, Touch Event, DispatchSemaphore","type":"article"},{"path":"\/documentation\/arinote\/211226_associated_type,_responder_chain,_touch_event,_run_loop","title":"211226 associated type, Responder Chain, Touch Event, Run Loop","type":"article"},{"path":"\/documentation\/arinote\/211222_uicollectionviewflowlayout,_pop","title":"211222 UICollectionViewFlowLayout, POP","type":"article"},{"path":"\/documentation\/arinote\/211221_collectionview,_unit_test","title":"211221 CollectionView, Unit Test","type":"article"},{"path":"\/documentation\/arinote\/211214_static,_factory_pattern,_visibleviewcontroller","title":"211214 static, Factory Pattern, visibleViewController.md","type":"article"},{"path":"\/documentation\/arinote\/211210_loadviewifneeded,_storyboard_reference","title":"211210 loadviewifneeded, Storyboard Reference","type":"article"},{"path":"\/documentation\/arinote\/211209_-----,_tableview,_xib,_celldidselect,_select-_------,_indexpathforselectedrow","title":"211209 뷰의재사용, TableView, xib, celldidSelect, Select시 회색배경제거, indexPathForSelectedRow","type":"article"},{"path":"\/documentation\/arinote\/211208_navigation_bar,_uiimageview,_nsmutableattributedstring,_addattribute","title":"211208 Navigation Bar, UIImageView, NSMutableAttributedString, addAttribute","type":"article"},{"path":"\/documentation\/arinote\/211206_tableview_----,_json,_api","title":"211206 TableView 활동학습, JSON","type":"article"},{"path":"\/documentation\/arinote\/211203_tableview,_-----","title":"211203 TableView, 뷰의재사용","type":"article"},{"path":"\/documentation\/arinote\/211126_app_life_cycle,_cfgetretaincount,_readme","title":"211126 App Life Cycle, CFGetRetainCount, README","type":"article"},{"path":"\/documentation\/arinote\/211125_auto_layout,_view_life_cycle,_staticstring,_fork--_repository_-----","title":"211125 Auto Layout, View Life Cycle, StaticString, fork해온 repository 잔디적용법","type":"article"},{"path":"\/documentation\/arinote\/211123_app_life_cycle,_init(frame:),_init(coder:),_frame,_bounds,_iboutlet_didset,_updateviewconstraints","title":"211123 App Life Cycle, init(frame:), init(coder:), frame, bounds, IBOutlet didSet, updateViewConstraints","type":"article"},{"path":"\/documentation\/arinote\/211122_arc,_oslog,_debugprint,_assert,_initializer,_scrollrecttovisible,_cgrect","title":"211122 ARC, OSLog, debugPrint, assert, initializer, scrollRectToVisible, CGRect","type":"article"},{"path":"\/documentation\/arinote\/211119_viewdidload,_localizederror,_scrollview_bar_hide,_addsubview,_ibinspectable,_cornerradius,_viewwilllayoutsubviews,_private_extension","title":"211119 viewDidLoad, LocalizedError, ScrollView bar hide, addSubView, IBInspectable, cornerRadius, viewWillLayoutSubviews, private extension","type":"article"},{"path":"\/documentation\/arinote\/211118_functional_programming,_higher-order_function,_main_run_loop,_update_cycle,_uiview,_attribute","title":"211118 Functional Programming, Higher-order function, Main Run Loop, Update Cycle, UIView, attribute","type":"article"},{"path":"\/documentation\/arinote\/211116_scroll_view,_stack_view,_setcontentoffset,_layoutifneeded,_setneedslayout","title":"211116 Scroll View, Stack View, setContentOffset, layoutIfNeeded, setNeedsLayout","type":"article"},{"path":"\/documentation\/arinote\/211105_outlet_collection,_extension,_---_--_--,_---_--","title":"211105 Outlet Collection, extension, 험난한 협업 과정, 이유와 근거","type":"article"},{"path":"\/documentation\/arinote\/211104_tdd,_unit_test,_@discardableresult,_cell_style,_droplast(),_removelast(),_poplast()","title":"211104 TDD, Unit Test, @discardableResult, Cell Style, dropLast(), removeLast(), popLast()","type":"article"},{"path":"\/documentation\/arinote\/211103_tdd,_unit_test,_tableview","title":"211103 TDD, Unit Test, TableView","type":"article"},{"path":"\/documentation\/arinote\/211102_loadviewifneeded,_adjustsfontforcontentsizecategory,_adjustsfontsizetofitwidth,_uibutton,_navigationviewcontroller,_removearrangedsubview","title":"211102 loadViewIfNeeded, adjustsFontForContentSizeCategory, adjustsFontSizeToFitWidth, UIButton, NavigationViewController, removearrangedsubview","type":"article"},{"path":"\/documentation\/arinote\/211101_kvo,_property_observer,_prepare,_loadviewifneeded,_nsobject,_uml,_auto_layout","title":"211101 KVO, Property Observer, prepare, loadViewIfNeeded, NSObject, UML, Auto Layout","type":"article"},{"path":"\/documentation\/arinote\/211030_auto_layout","title":"211030 Auto Layout","type":"article"},{"path":"\/documentation\/arinote\/211029_viewcontroller,_life_cycle,_notificationcenter,_deinit,_bundle,_preview","title":"211029 ViewController, Life Cycle, NotificationCenter, deinit, Bundle, preview","type":"article"},{"path":"\/documentation\/arinote\/211028_uistepper,_auto_layout,_prepare,_extension,_customstringconvertible,_nsbundle,_uibutton","title":"211028 UIStepper, auto layout, prepare, extension, CustomStringConvertible, NSBundle, UIButton","type":"article"},{"path":"\/documentation\/arinote\/211026_device_orientation,_navigationviewcontroller,_madal,_gesture,_naming","title":"211026 Device Orientation, NavigationViewController, madal, Gesture, Naming","type":"article"},{"path":"\/documentation\/arinote\/211025_uinavigationbar,_localizederror,_self,_self,_protocol,_extension,_typecasting,_optional,_dismiss","title":"211025 UINavigationBar, LocalizedError, self, Self, protocol, extension, TypeCasting, Optional, dismiss","type":"article"},{"path":"\/documentation\/arinote\/211023_initialization,_mvc,_notification_--","title":"211023 Initialization, MVC, Notification 복습","type":"article"},{"path":"\/documentation\/arinote\/211022_viewcontroller,_navigation_controller,_life_cycle","title":"211022 ViewController, Navigation Controller, Life cycle","type":"article"},{"path":"\/documentation\/arinote\/211021_initalizer,_hig,_notification,_alert,_segue,_storyboard","title":"211021 initalizer, HIG, Notification, Alert, Segue, Storyboard","type":"article"},{"path":"\/documentation\/arinote\/211014_hig,_----,_---,_thread,_serial_queue,_xcode_debugging","title":"211014 HIG, 재귀함수, 반복문, Thread, Serial Queue, Xcode debugging","type":"article"},{"path":"\/documentation\/arinote\/210807_----_--_ios_---","title":"210807 왕초보를 위한 IOS 앱개발","type":"article"},{"path":"\/documentation\/arinote\/210806_----_--_ios_---","title":"210806 왕초보를 위한 IOS 앱개발","type":"article"}],"path":"\/documentation\/arinote\/uikit","title":"UIKit","type":"symbol"},{"children":[{"path":"\/documentation\/arinote\/240104_userdefaults,_xcode_find_and_replace_regular_expression","title":"240104 UserDefaults, Xcode Find and Replace Regular Expression","type":"article"},{"path":"\/documentation\/arinote\/220321_app_distribution,_hashable","title":"220321 App Distribution, Hashable","type":"article"},{"path":"\/documentation\/arinote\/211220_---_----,_----","title":"211220 의존성 관리도구, 오픈소스","type":"article"},{"path":"\/documentation\/arinote\/211213_accessibility,_dynamic_type,_voice_over,_fatalerror,_---_-----,_vim","title":"211213 Accessibility, Dynamic Type, Voice over, fatalError, 함수형 프로그래밍, Vim","type":"article"},{"path":"\/documentation\/arinote\/211115_lldb,_complie,_build,_link,_split,_components,_import,_get","title":"211115 LLDB, Complie, Build, Link, split, components, import, get","type":"article"},{"path":"\/documentation\/arinote\/211015_lldb,_gcd,_thread,_serial_queue,_async","title":"211015 LLDB, GCD, Thread, Serial Queue, Async","type":"article"}],"path":"\/documentation\/arinote\/xcode","title":"Xcode","type":"symbol"},{"children":[{"path":"\/documentation\/arinote\/220324_-----_----","title":"220324 디자인패턴 활동학습","type":"article"},{"path":"\/documentation\/arinote\/220322_flyweight,_facade","title":"220322 Flyweight, Facade","type":"article"},{"path":"\/documentation\/arinote\/220303_clean_architecture_mvvm,_async,_await","title":"220303 Clean Architecture MVVM, async, await","type":"article"},{"path":"\/documentation\/arinote\/220301_mvvm,_----_--","title":"220301 MVVM, 프로젝트 설계","type":"article"}],"path":"\/documentation\/arinote\/architecture","title":"Architecture","type":"symbol"}],"path":"\/documentation\/arinote","title":"Ari's Notes","type":"module"}]},"schemaVersion":{"major":0,"minor":1,"patch":2}} \ No newline at end of file diff --git a/docs/js/104.35ce41d9.js b/docs/js/104.35ce41d9.js new file mode 100644 index 0000000..8095ba0 --- /dev/null +++ b/docs/js/104.35ce41d9.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +"use strict";(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[104],{6137:function(e,t,n){n.d(t,{Z:function(){return d}});var r=function(){var e=this,t=e._self._c;return t("span",{staticClass:"badge",class:{[`badge-${e.variant}`]:e.variant},attrs:{role:"presentation"}},[e._t("default",(function(){return[e._v(e._s(e.text?e.$t(e.text):""))]}))],2)},a=[];const i={beta:"aside-kind.beta",deprecated:"aside-kind.deprecated"};var s={name:"Badge",props:{variant:{type:String,default:()=>""}},computed:{text:({variant:e})=>i[e]}},o=s,l=n(1001),c=(0,l.Z)(o,r,a,!1,null,"04624022",null),d=c.exports},8846:function(e,t,n){n.d(t,{Z:function(){return d}});var r=function(){var e=this,t=e._self._c;return t("BaseContentNode",e._b({},"BaseContentNode",e.$props,!1))},a=[],i=n(9519),s={name:"ContentNode",components:{BaseContentNode:i["default"]},props:i["default"].props,methods:i["default"].methods,BlockType:i["default"].BlockType,InlineType:i["default"].InlineType},o=s,l=n(1001),c=(0,l.Z)(o,r,a,!1,null,"3a32ffd0",null),d=c.exports},7120:function(e,t,n){n.d(t,{Z:function(){return c}});var r=function(e,t){return e("p",{staticClass:"requirement-metadata",class:t.data.staticClass},[e("strong",[t._v(t._s(t.parent.$t("required")))]),t.props.defaultImplementationsCount?[t._v(" "+t._s(t.parent.$tc("metadata.default-implementation",t.props.defaultImplementationsCount))+" ")]:t._e()],2)},a=[],i={name:"RequirementMetadata",props:{defaultImplementationsCount:{type:Number,default:0}}},s=i,o=n(1001),l=(0,o.Z)(s,r,a,!0,null,null,null),c=l.exports},4165:function(e,t,n){n.d(t,{default:function(){return R}});var r,a,i,s,o,l,c,d,p=n(352),u={name:"ChangedToken",render(e){const{kind:t,tokens:n}=this;return e("span",{class:[`token-${t}`,"token-changed"]},n.map((t=>e(R,{props:t}))))},props:{kind:{type:String,required:!0},tokens:{type:Array,required:!0}}},f=u,h=n(1001),m=(0,h.Z)(f,r,a,!1,null,null,null),g=m.exports,k=n(9882),y=n(5953),v={name:"LinkableToken",mixins:[y.Z],render(e){const t=this.references[this.identifier];return t&&t.url?e(k.Z,{props:{url:t.url,kind:t.kind,role:t.role}},this.$slots.default):e("span",{},this.$slots.default)},props:{identifier:{type:String,required:!0,default:()=>""}}},b=v,_=(0,h.Z)(b,i,s,!1,null,null,null),C=_.exports,x={name:"RawText",render(e){const{_v:t=(t=>e("span",t)),text:n}=this;return t(n)},props:{text:{type:String,required:!0}}},Z=x,B=(0,h.Z)(Z,o,l,!1,null,null,null),T=B.exports,S={name:"SyntaxToken",render(e){return e("span",{class:`token-${this.kind}`},this.text)},props:{kind:{type:String,required:!0},text:{type:String,required:!0}}},I=S,$=(0,h.Z)(I,c,d,!1,null,null,null),q=$.exports,w=function(){var e=this,t=e._self._c;return t("strong",{staticClass:"highlighted"},[e._t("default")],2)},L=[],A={name:"Highlighted"},P=A,D=(0,h.Z)(P,w,L,!1,null,"798bdf9c",null),F=D.exports;const O={attribute:"attribute",externalParam:"externalParam",genericParameter:"genericParameter",identifier:"identifier",internalParam:"internalParam",keyword:"keyword",label:"label",number:"number",string:"string",text:"text",typeIdentifier:"typeIdentifier",highlightDiff:"highlightDiff",added:"added",removed:"removed"};var z,M,N={name:"DeclarationToken",render:function e(t){const{kind:n,text:r,tokens:a}=this;switch(n){case O.text:{const e={text:r};return t(T,{props:e})}case O.typeIdentifier:{const e={identifier:this.identifier};return t(C,{class:"type-identifier-link",props:e},[t(p.Z,r)])}case O.attribute:{const{identifier:e}=this;return e?t(C,{class:"attribute-link",props:{identifier:e}},[t(p.Z,r)]):t(q,{props:{kind:n,text:r}})}case O.added:case O.removed:return t(g,{props:{tokens:a,kind:n}});case O.highlightDiff:return t(F,{},(a||[]).map((n=>e.bind(n)(t))));default:{const e={kind:n,text:r};return t(q,{props:e})}}},constants:{TokenKind:O},props:{kind:{type:String,required:!0},identifier:{type:String,required:!1},text:{type:String,required:!1},tokens:{type:Array,required:!1,default:()=>[]}}},V=N,j=(0,h.Z)(V,z,M,!1,null,"b15299ea",null),R=j.exports},2970:function(e,t,n){n.d(t,{Z:function(){return $}});var r=function(){var e=this,t=e._self._c;return e.icon?t("div",{staticClass:"topic-icon-wrapper"},[t(e.icon,{tag:"component",staticClass:"topic-icon"})],1):e._e()},a=[],i=n(5692),s=n(7775),o=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"api-reference-icon",attrs:{viewBox:"0 0 14 14",themeId:"api-reference"}},[t("title",[e._v(e._s(e.$t("api-reference")))]),t("path",{attrs:{d:"m1 1v12h12v-12zm11 11h-10v-10h10z"}}),t("path",{attrs:{d:"m3 4h8v1h-8zm0 2.5h8v1h-8zm0 2.5h8v1h-8z"}}),t("path",{attrs:{d:"m3 4h8v1h-8z"}}),t("path",{attrs:{d:"m3 6.5h8v1h-8z"}}),t("path",{attrs:{d:"m3 9h8v1h-8z"}})])},l=[],c=n(9742),d={name:"APIReferenceIcon",components:{SVGIcon:c.Z}},p=d,u=n(1001),f=(0,u.Z)(p,o,l,!1,null,null,null),h=f.exports,m=function(){var e=this,t=e._self._c;return t("SVGIcon",{attrs:{viewBox:"0 0 14 14",themeId:"endpoint"}},[t("title",[e._v(e._s(e.$t("icons.web-service-endpoint")))]),t("path",{attrs:{d:"M4.052 8.737h-1.242l-1.878 5.263h1.15l0.364-1.081h1.939l0.339 1.081h1.193zM2.746 12.012l0.678-2.071 0.653 2.071z"}}),t("path",{attrs:{d:"M11.969 8.737h1.093v5.263h-1.093v-5.263z"}}),t("path",{attrs:{d:"M9.198 8.737h-2.295v5.263h1.095v-1.892h1.12c0.040 0.003 0.087 0.004 0.134 0.004 0.455 0 0.875-0.146 1.217-0.394l-0.006 0.004c0.296-0.293 0.48-0.699 0.48-1.148 0-0.060-0.003-0.118-0.010-0.176l0.001 0.007c0.003-0.039 0.005-0.085 0.005-0.131 0-0.442-0.183-0.842-0.476-1.128l-0-0c-0.317-0.256-0.724-0.41-1.168-0.41-0.034 0-0.069 0.001-0.102 0.003l0.005-0zM9.628 11.014c-0.15 0.118-0.341 0.188-0.548 0.188-0.020 0-0.040-0.001-0.060-0.002l0.003 0h-1.026v-1.549h1.026c0.017-0.001 0.037-0.002 0.058-0.002 0.206 0 0.396 0.066 0.551 0.178l-0.003-0.002c0.135 0.13 0.219 0.313 0.219 0.515 0 0.025-0.001 0.050-0.004 0.074l0-0.003c0.002 0.020 0.003 0.044 0.003 0.068 0 0.208-0.083 0.396-0.219 0.534l0-0z"}}),t("path",{attrs:{d:"M13.529 4.981c0-1.375-1.114-2.489-2.489-2.49h-0l-0.134 0.005c-0.526-1.466-1.903-2.496-3.522-2.496-0.892 0-1.711 0.313-2.353 0.835l0.007-0.005c-0.312-0.243-0.709-0.389-1.14-0.389-1.030 0-1.865 0.834-1.866 1.864v0c0 0.001 0 0.003 0 0.004 0 0.123 0.012 0.242 0.036 0.358l-0.002-0.012c-0.94 0.37-1.593 1.27-1.593 2.323 0 1.372 1.11 2.485 2.482 2.49h8.243c1.306-0.084 2.333-1.164 2.333-2.484 0-0.001 0-0.002 0-0.003v0zM11.139 6.535h-8.319c-0.799-0.072-1.421-0.739-1.421-1.551 0-0.659 0.41-1.223 0.988-1.45l0.011-0.004 0.734-0.28-0.148-0.776-0.012-0.082v-0.088c0-0 0-0.001 0-0.001 0-0.515 0.418-0.933 0.933-0.933 0.216 0 0.416 0.074 0.574 0.197l-0.002-0.002 0.584 0.453 0.575-0.467 0.169-0.127c0.442-0.306 0.991-0.489 1.581-0.489 1.211 0 2.243 0.769 2.633 1.846l0.006 0.019 0.226 0.642 0.814-0.023 0.131 0.006c0.805 0.067 1.432 0.736 1.432 1.552 0 0.836-0.659 1.518-1.486 1.556l-0.003 0z"}})])},g=[],k={name:"EndpointIcon",components:{SVGIcon:c.Z}},y=k,v=(0,u.Z)(y,m,g,!1,null,null,null),b=v.exports,_=n(8633),C=n(9001),x=n(8638),Z=n(7192);const B={[Z.L.article]:i.Z,[Z.L.collection]:C.Z,[Z.L.collectionGroup]:h,[Z.L.learn]:_.Z,[Z.L.overview]:_.Z,[Z.L.project]:x.Z,[Z.L.tutorial]:x.Z,[Z.L.resources]:_.Z,[Z.L.sampleCode]:s.Z,[Z.L.restRequestSymbol]:b};var T={components:{SVGIcon:c.Z},props:{role:{type:String,required:!0}},computed:{icon:({role:e})=>B[e]}},S=T,I=(0,u.Z)(S,r,a,!1,null,"55f9d05d",null),$=I.exports},8104:function(e,t,n){n.r(t),n.d(t,{default:function(){return q}});var r=function(){var e=this,t=e._self._c;return t("div",{staticClass:"link-block",class:e.linkBlockClasses},[t(e.linkComponent,e._b({ref:"apiChangesDiff",tag:"component",staticClass:"link",class:e.linkClasses},"component",e.linkProps,!1),[e.topic.role&&!e.change?t("TopicLinkBlockIcon",{attrs:{role:e.topic.role}}):e._e(),e.topic.fragments?t("DecoratedTopicTitle",{attrs:{tokens:e.topic.fragments}}):t("WordBreak",{attrs:{tag:e.titleTag}},[e._v(e._s(e.topic.title))]),e.change?t("span",{staticClass:"visuallyhidden"},[e._v("- "+e._s(e.$t(e.changeName)))]):e._e()],1),e.hasAbstractElements?t("div",{staticClass:"abstract"},[e.topic.abstract?t("ContentNode",{attrs:{content:e.topic.abstract}}):e._e(),e.topic.ideTitle?t("div",{staticClass:"topic-keyinfo"},[e.topic.titleStyle===e.titleStyles.title?[t("strong",[e._v("Key:")]),e._v(" "+e._s(e.topic.name)+" ")]:e.topic.titleStyle===e.titleStyles.symbol?[t("strong",[e._v("Name:")]),e._v(" "+e._s(e.topic.ideTitle)+" ")]:e._e()],2):e._e(),e.topic.required||e.topic.defaultImplementations?t("RequirementMetadata",{staticClass:"topic-required",attrs:{defaultImplementationsCount:e.topic.defaultImplementations}}):e._e()],1):e._e(),e.showDeprecatedBadge?t("Badge",{attrs:{variant:"deprecated"}}):e.showBetaBadge?t("Badge",{attrs:{variant:"beta"}}):e._e(),e._l(e.tags,(function(n){return t("Badge",{key:`${n.type}-${n.text}`,attrs:{variant:n.type}},[e._v(" "+e._s(n.text)+" ")])}))],2)},a=[],i=n(7192),s=n(2449),o=n(6137),l=n(352),c=n(8846),d=n(2970),p=function(){var e=this,t=e._self._c;return t("code",{staticClass:"decorated-title"},[e._l(e.tokens,(function(n,r){return[t(e.componentFor(n),{key:r,tag:"component",class:[e.classFor(n),e.emptyTokenClass(n)]},[e._v(e._s(n.text))]),t("wbr",{key:`wbr-${r}`})]}))],2)},u=[],f=n(4165);const{TokenKind:h}=f["default"].constants,m={decorator:"decorator",identifier:"identifier",label:"label"};var g={name:"DecoratedTopicTitle",components:{WordBreak:l.Z},props:{tokens:{type:Array,required:!0,default:()=>[]}},constants:{TokenKind:h},methods:{emptyTokenClass:({text:e})=>({"empty-token":" "===e}),classFor({kind:e}){switch(e){case h.externalParam:case h.identifier:return m.identifier;case h.label:return m.label;default:return m.decorator}},componentFor(e){return/^\s+$/.test(e.text)?"span":l.Z}}},k=g,y=n(1001),v=(0,y.Z)(k,p,u,!1,null,"17c84f82",null),b=v.exports,_=n(7120),C=n(1842),x=n(5953);const Z={article:"article",symbol:"symbol"},B={title:"title",symbol:"symbol"},T={link:"link"};var S={name:"TopicsLinkBlock",components:{Badge:o.Z,WordBreak:l.Z,ContentNode:c.Z,TopicLinkBlockIcon:d.Z,DecoratedTopicTitle:b,RequirementMetadata:_.Z},mixins:[C.JY,C.PH,x.Z],constants:{ReferenceType:T,TopicKind:Z,TitleStyles:B},props:{isSymbolBeta:Boolean,isSymbolDeprecated:Boolean,topic:{type:Object,required:!0,validator:e=>(!("abstract"in e)||Array.isArray(e.abstract))&&"string"===typeof e.identifier&&(e.type===T.link&&!e.kind||"string"===typeof e.kind)&&(e.type===T.link&&!e.role||"string"===typeof e.role)&&"string"===typeof e.title&&"string"===typeof e.url&&(!("defaultImplementations"in e)||"number"===typeof e.defaultImplementations)&&(!("required"in e)||"boolean"===typeof e.required)}},data(){return{state:this.store.state}},computed:{linkComponent:({topic:e})=>e.type===T.link?"a":"router-link",linkProps({topic:e}){const t=(0,s.Q2)(e.url,this.$route.query);return e.type===T.link?{href:t}:{to:t}},linkBlockClasses:({changesClasses:e,hasAbstractElements:t,displaysMultipleLinesAfterAPIChanges:n,multipleLinesClass:r})=>({"has-inline-element":!t,[r]:n,...!t&&e}),linkClasses:({changesClasses:e,deprecated:t,hasAbstractElements:n})=>({deprecated:t,"has-adjacent-elements":n,...n&&e}),changesClasses:({getChangesClasses:e,change:t})=>e(t),titleTag({topic:e}){if(e.titleStyle===B.title)return e.ideTitle?"span":"code";if(e.role&&(e.role===i.L.collection||e.role===i.L.dictionarySymbol))return"span";switch(e.kind){case Z.symbol:return"code";default:return"span"}},titleStyles:()=>B,deprecated:({showDeprecatedBadge:e,topic:t})=>e||t.deprecated,showBetaBadge:({topic:e,isSymbolBeta:t})=>Boolean(!t&&e.beta),showDeprecatedBadge:({topic:e,isSymbolDeprecated:t})=>Boolean(!t&&e.deprecated),change({topic:{identifier:e},state:{apiChanges:t}}){return this.changeFor(e,t)},changeName:({change:e,getChangeName:t})=>t(e),hasAbstractElements:({topic:{abstract:e,required:t,defaultImplementations:n}}={})=>e&&e.length>0||t||n,tags:({topic:e})=>(e.tags||[]).slice(0,1),iconOverride:({topic:{images:e=[]}})=>{const t=e.find((({type:e})=>"icon"===e));return t?t.identifier:null}}},I=S,$=(0,y.Z)(I,r,a,!1,null,"0d9c6bcc",null),q=$.exports},4733:function(e,t,n){n.d(t,{_:function(){return r}});const r="displays-multiple-lines"},1842:function(e,t,n){n.d(t,{JY:function(){return c},PH:function(){return l}});var r=n(9426),a=n(4733),i=n(3112);const s="latest_",o={xcode:{value:"xcode",label:"Xcode"},other:{value:"other",label:"Other"}},l={constants:{multipleLinesClass:a._},data(){return{multipleLinesClass:a._}},computed:{displaysMultipleLinesAfterAPIChanges:({change:e,changeType:t,$refs:n})=>!(!e&&!t)&&(0,i.s)(n.apiChangesDiff)}},c={methods:{toVersionRange({platform:e,versions:t}){return`${e} ${t[0]} – ${e} ${t[1]}`},toOptionValue:e=>`${s}${e}`,toScope:e=>e.slice(s.length,e.length),getOptionsForDiffAvailability(e={}){return this.getOptionsForDiffAvailabilities([e])},getOptionsForDiffAvailabilities(e=[]){const t=e.reduce(((e,t={})=>Object.keys(t).reduce(((e,n)=>({...e,[n]:(e[n]||[]).concat(t[n])})),e)),{}),n=Object.keys(t),r=n.reduce(((e,n)=>{const r=t[n];return{...e,[n]:r.find((e=>e.platform===o.xcode.label))||r[0]}}),{}),a=e=>({label:this.toVersionRange(r[e]),value:this.toOptionValue(e),platform:r[e].platform}),{sdk:i,beta:s,minor:l,major:c,...d}=r,p=[].concat(i?a("sdk"):[]).concat(s?a("beta"):[]).concat(l?a("minor"):[]).concat(c?a("major"):[]).concat(Object.keys(d).map(a));return this.splitOptionsPerPlatform(p)},changesClassesFor(e,t){const n=this.changeFor(e,t);return this.getChangesClasses(n)},getChangesClasses:e=>({[`changed changed-${e}`]:!!e}),changeFor(e,t){const{change:n}=(t||{})[e]||{};return n},splitOptionsPerPlatform(e){return e.reduce(((e,t)=>{const n=t.platform===o.xcode.label?o.xcode.value:o.other.value;return e[n].push(t),e}),{[o.xcode.value]:[],[o.other.value]:[]})},getChangeName(e){return r.Ag[e]}},computed:{availableOptions({diffAvailability:e={},toOptionValue:t}){return new Set(Object.keys(e).map(t))}}}},3112:function(e,t,n){function r(e){if(!e)return!1;const t=window.getComputedStyle(e.$el||e),n=(e.$el||e).offsetHeight,r=t.lineHeight?parseFloat(t.lineHeight):1,a=t.paddingTop?parseFloat(t.paddingTop):0,i=t.paddingBottom?parseFloat(t.paddingBottom):0,s=t.borderTopWidth?parseFloat(t.borderTopWidth):0,o=t.borderBottomWidth?parseFloat(t.borderBottomWidth):0,l=n-(a+i+s+o),c=l/r;return c>=2}n.d(t,{s:function(){return r}})}}]); \ No newline at end of file diff --git a/docs/js/337.274a8ccc.js b/docs/js/337.274a8ccc.js new file mode 100644 index 0000000..460601c --- /dev/null +++ b/docs/js/337.274a8ccc.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[337],{6337:function(){(function(){"use strict";if("object"===typeof window)if("IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype)"isIntersecting"in window.IntersectionObserverEntry.prototype||Object.defineProperty(window.IntersectionObserverEntry.prototype,"isIntersecting",{get:function(){return this.intersectionRatio>0}});else{var t=function(t){var e=t,n=i(e);while(n)e=n.ownerDocument,n=i(e);return e}(window.document),e=[],n=null,o=null;s.prototype.THROTTLE_TIMEOUT=100,s.prototype.POLL_INTERVAL=null,s.prototype.USE_MUTATION_OBSERVER=!0,s._setupCrossOriginUpdater=function(){return n||(n=function(t,n){o=t&&n?g(t,n):p(),e.forEach((function(t){t._checkForIntersections()}))}),n},s._resetCrossOriginUpdater=function(){n=null,o=null},s.prototype.observe=function(t){var e=this._observationTargets.some((function(e){return e.element==t}));if(!e){if(!t||1!=t.nodeType)throw new Error("target must be an Element");this._registerInstance(),this._observationTargets.push({element:t,entry:null}),this._monitorIntersections(t.ownerDocument),this._checkForIntersections()}},s.prototype.unobserve=function(t){this._observationTargets=this._observationTargets.filter((function(e){return e.element!=t})),this._unmonitorIntersections(t.ownerDocument),0==this._observationTargets.length&&this._unregisterInstance()},s.prototype.disconnect=function(){this._observationTargets=[],this._unmonitorAllIntersections(),this._unregisterInstance()},s.prototype.takeRecords=function(){var t=this._queuedEntries.slice();return this._queuedEntries=[],t},s.prototype._initThresholds=function(t){var e=t||[0];return Array.isArray(e)||(e=[e]),e.sort().filter((function(t,e,n){if("number"!=typeof t||isNaN(t)||t<0||t>1)throw new Error("threshold must be a number between 0 and 1 inclusively");return t!==n[e-1]}))},s.prototype._parseRootMargin=function(t){var e=t||"0px",n=e.split(/\s+/).map((function(t){var e=/^(-?\d*\.?\d+)(px|%)$/.exec(t);if(!e)throw new Error("rootMargin must be specified in pixels or percent");return{value:parseFloat(e[1]),unit:e[2]}}));return n[1]=n[1]||n[0],n[2]=n[2]||n[0],n[3]=n[3]||n[1],n},s.prototype._monitorIntersections=function(e){var n=e.defaultView;if(n&&-1==this._monitoringDocuments.indexOf(e)){var o=this._checkForIntersections,r=null,s=null;this.POLL_INTERVAL?r=n.setInterval(o,this.POLL_INTERVAL):(c(n,"resize",o,!0),c(e,"scroll",o,!0),this.USE_MUTATION_OBSERVER&&"MutationObserver"in n&&(s=new n.MutationObserver(o),s.observe(e,{attributes:!0,childList:!0,characterData:!0,subtree:!0}))),this._monitoringDocuments.push(e),this._monitoringUnsubscribes.push((function(){var t=e.defaultView;t&&(r&&t.clearInterval(r),a(t,"resize",o,!0)),a(e,"scroll",o,!0),s&&s.disconnect()}));var h=this.root&&(this.root.ownerDocument||this.root)||t;if(e!=h){var u=i(e);u&&this._monitorIntersections(u.ownerDocument)}}},s.prototype._unmonitorIntersections=function(e){var n=this._monitoringDocuments.indexOf(e);if(-1!=n){var o=this.root&&(this.root.ownerDocument||this.root)||t,r=this._observationTargets.some((function(t){var n=t.element.ownerDocument;if(n==e)return!0;while(n&&n!=o){var r=i(n);if(n=r&&r.ownerDocument,n==e)return!0}return!1}));if(!r){var s=this._monitoringUnsubscribes[n];if(this._monitoringDocuments.splice(n,1),this._monitoringUnsubscribes.splice(n,1),s(),e!=o){var h=i(e);h&&this._unmonitorIntersections(h.ownerDocument)}}}},s.prototype._unmonitorAllIntersections=function(){var t=this._monitoringUnsubscribes.slice(0);this._monitoringDocuments.length=0,this._monitoringUnsubscribes.length=0;for(var e=0;e=0&&h>=0&&{top:n,bottom:o,left:i,right:r,width:s,height:h}||null}function f(t){var e;try{e=t.getBoundingClientRect()}catch(n){}return e?(e.width&&e.height||(e={top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:e.right-e.left,height:e.bottom-e.top}),e):p()}function p(){return{top:0,bottom:0,left:0,right:0,width:0,height:0}}function d(t){return!t||"x"in t?t:{top:t.top,y:t.top,bottom:t.bottom,left:t.left,x:t.left,right:t.right,width:t.width,height:t.height}}function g(t,e){var n=e.top-t.top,o=e.left-t.left;return{top:n,left:o,height:e.height,width:e.width,bottom:n+e.height,right:o+e.width}}function m(t,e){var n=e;while(n){if(n==t)return!0;n=_(n)}return!1}function _(e){var n=e.parentNode;return 9==e.nodeType&&e!=t?i(e):(n&&n.assignedSlot&&(n=n.assignedSlot.parentNode),n&&11==n.nodeType&&n.host?n.host:n)}function v(t){return t&&9===t.nodeType}})()}}]); \ No newline at end of file diff --git a/docs/js/842.bf5508df.js b/docs/js/842.bf5508df.js new file mode 100644 index 0000000..87975b1 --- /dev/null +++ b/docs/js/842.bf5508df.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +"use strict";(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[842],{5590:function(t,e,n){n.d(e,{Z:function(){return m}});var s=function(){var t=this,e=t._self._c;return e("PortalSource",{attrs:{to:"modal-destination",disabled:!t.isVisible}},[e("div",{directives:[{name:"show",rawName:"v-show",value:t.isVisible,expression:"isVisible"}],staticClass:"generic-modal",class:[t.stateClasses,t.themeClass],style:t.modalColors,attrs:{role:"dialog"}},[e("div",{staticClass:"backdrop",on:{click:t.onClickOutside}}),e("div",{ref:"container",staticClass:"container",style:{width:t.width}},[t.showClose?e("button",{ref:"close",staticClass:"close",attrs:{"aria-label":t.$t("verbs.close")},on:{click:function(e){return e.preventDefault(),t.closeModal.apply(null,arguments)}}},[e("CloseIcon")],1):t._e(),e("div",{ref:"content",staticClass:"modal-content"},[t._t("default")],2)])])])},r=[],o=n(9652),i=n(114),a=n(1147),l=n(2433),c=n(1970);const u={light:"light",dark:"dark",dynamic:"dynamic",code:"code"};var h={name:"GenericModal",model:{prop:"visible",event:"update:visible"},components:{CloseIcon:c.Z,PortalSource:l.h_},props:{visible:{type:Boolean,default:!1},isFullscreen:{type:Boolean,default:!1},theme:{type:String,validator:t=>Object.keys(u).includes(t),default:u.light},codeBackgroundColorOverride:{type:String,default:""},backdropBackgroundColorOverride:{type:String,default:""},width:{type:String,default:null},showClose:{type:Boolean,default:!0}},data(){return{lastFocusItem:null,prefersDarkStyle:!1,focusTrapInstance:null}},computed:{isVisible:{get:({visible:t})=>t,set(t){this.$emit("update:visible",t)}},modalColors(){return{"--code-background":this.codeBackgroundColorOverride,"--backdrop-background":this.backdropBackgroundColorOverride}},themeClass({theme:t,prefersDarkStyle:e,isThemeDynamic:n}){let s={};return n&&(s={"theme-light":!e,"theme-dark":e}),[`theme-${t}`,s]},stateClasses:({isFullscreen:t,isVisible:e,showClose:n})=>({"modal-fullscreen":t,"modal-standard":!t,"modal-open":e,"modal-with-close":n}),isThemeDynamic:({theme:t})=>t===u.dynamic||t===u.code},watch:{isVisible(t){t?this.onShow():this.onHide()}},mounted(){if(this.focusTrapInstance=new i.Z,document.addEventListener("keydown",this.onKeydown),this.isThemeDynamic){const t=window.matchMedia("(prefers-color-scheme: dark)");t.addListener(this.onColorSchemePreferenceChange),this.$once("hook:beforeDestroy",(()=>{t.removeListener(this.onColorSchemePreferenceChange)})),this.onColorSchemePreferenceChange(t)}},beforeDestroy(){this.isVisible&&o.Z.unlockScroll(this.$refs.container),document.removeEventListener("keydown",this.onKeydown),this.focusTrapInstance.destroy()},methods:{async onShow(){await this.$nextTick(),o.Z.lockScroll(this.$refs.container),await this.focusCloseButton(),this.focusTrapInstance.updateFocusContainer(this.$refs.container),this.focusTrapInstance.start(),a.Z.hide(this.$refs.container)},onHide(){o.Z.unlockScroll(this.$refs.container),this.focusTrapInstance.stop(),this.lastFocusItem&&(this.lastFocusItem.focus({preventScroll:!0}),this.lastFocusItem=null),this.$emit("close"),a.Z.show(this.$refs.container)},closeModal(){this.isVisible=!1},selectContent(){window.getSelection().selectAllChildren(this.$refs.content)},onClickOutside(){this.closeModal()},onKeydown(t){const{metaKey:e=!1,ctrlKey:n=!1,key:s}=t;this.isVisible&&("a"===s&&(e||n)&&(t.preventDefault(),this.selectContent()),"Escape"===s&&(t.preventDefault(),this.closeModal()))},onColorSchemePreferenceChange({matches:t}){this.prefersDarkStyle=t},async focusCloseButton(){this.lastFocusItem=document.activeElement,await this.$nextTick(),this.$refs.close&&this.$refs.close.focus(),this.$emit("open")}}},d=h,f=n(1001),p=(0,f.Z)(d,s,r,!1,null,"795f7b59",null),m=p.exports},5151:function(t,e,n){n.d(e,{Z:function(){return u}});var s=function(){var t=this,e=t._self._c;return e("SVGIcon",{staticClass:"inline-chevron-down-icon",attrs:{viewBox:"0 0 14 14",themeId:"inline-chevron-down"}},[e("path",{attrs:{d:"M12.634 2.964l0.76 0.649-6.343 7.426-6.445-7.423 0.755-0.655 5.683 6.545 5.59-6.542z"}})])},r=[],o=n(9742),i={name:"InlineChevronDownIcon",components:{SVGIcon:o.Z}},a=i,l=n(1001),c=(0,l.Z)(a,s,r,!1,null,null,null),u=c.exports},6772:function(t,e,n){n.d(e,{Z:function(){return u}});var s=function(){var t=this,e=t._self._c;return e("SVGIcon",{staticClass:"inline-plus-circle-icon",attrs:{viewBox:"0 0 14 14",themeId:"inline-plus-circle"}},[e("path",{attrs:{d:"M7 0.5c3.59 0 6.5 2.91 6.5 6.5s-2.91 6.5-6.5 6.5c-3.59 0-6.5-2.91-6.5-6.5v0c0-3.59 2.91-6.5 6.5-6.5v0zM7 1.5c-3.038 0-5.5 2.462-5.5 5.5s2.462 5.5 5.5 5.5c3.038 0 5.5-2.462 5.5-5.5v0c0-3.038-2.462-5.5-5.5-5.5v0z"}}),e("path",{attrs:{d:"M4 6.52h6v1h-6v-1z"}}),e("path",{attrs:{d:"M6.5 4.010h1v6h-1v-6z"}})])},r=[],o=n(9742),i={name:"InlinePlusCircleIcon",components:{SVGIcon:o.Z}},a=i,l=n(1001),c=(0,l.Z)(a,s,r,!1,null,null,null),u=c.exports},8093:function(t,e,n){n.d(e,{Z:function(){return y}});var s=function(){var t=this,e=t._self._c;return e("div",{style:t.codeStyle},[t._t("default")],2)},r=[],o=n(8571);const i=0,a=255;function l(t){const e=t.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*(\d+\.?\d*|\.\d+)\s*\)/);if(!e)throw new Error("invalid rgba() input");const n=10;return{r:parseInt(e[1],n),g:parseInt(e[2],n),b:parseInt(e[3],n),a:parseFloat(e[4])}}function c(t){const{r:e,g:n,b:s}=l(t);return.2126*e+.7152*n+.0722*s}function u(t,e){const n=Math.round(a*e),s=l(t),{a:r}=s,[o,c,u]=[s.r,s.g,s.b].map((t=>Math.max(i,Math.min(a,t+n))));return`rgba(${o}, ${c}, ${u}, ${r})`}function h(t,e){return u(t,e)}function d(t,e){return u(t,-1*e)}var f={name:"CodeTheme",data(){return{codeThemeState:o.Z.state}},computed:{codeStyle(){const{codeColors:t}=this.codeThemeState;return t?{"--text":t.text,"--background":t.background,"--line-highlight":t.lineHighlight,"--url":t.commentURL,"--syntax-comment":t.comment,"--syntax-quote":t.comment,"--syntax-keyword":t.keyword,"--syntax-literal":t.keyword,"--syntax-selector-tag":t.keyword,"--syntax-string":t.stringLiteral,"--syntax-bullet":t.stringLiteral,"--syntax-meta":t.keyword,"--syntax-number":t.stringLiteral,"--syntax-symbol":t.stringLiteral,"--syntax-tag":t.stringLiteral,"--syntax-attr":t.typeAnnotation,"--syntax-built_in":t.typeAnnotation,"--syntax-builtin-name":t.typeAnnotation,"--syntax-class":t.typeAnnotation,"--syntax-params":t.typeAnnotation,"--syntax-section":t.typeAnnotation,"--syntax-title":t.typeAnnotation,"--syntax-type":t.typeAnnotation,"--syntax-attribute":t.keyword,"--syntax-identifier":t.text,"--syntax-subst":t.text,"--color-syntax-param-internal-name":this.internalParamNameColor}:null},internalParamNameColor(){const{background:t,text:e}=this.codeThemeState.codeColors;try{const n=c(t),s=n1&&void 0!==arguments[1]?arguments[1]:{};return t.reduce((function(t,n){var s=n.passengers[0],r="function"===typeof s?s(e):n.passengers;return t.concat(r)}),[])}function f(t,e){return t.map((function(t,e){return[e,t]})).sort((function(t,n){return e(t[1],n[1])||t[0]-n[0]})).map((function(t){return t[1]}))}function p(t,e){return e.reduce((function(e,n){return t.hasOwnProperty(n)&&(e[n]=t[n]),e}),{})}var m={},g={},y={},b=r.extend({data:function(){return{transports:m,targets:g,sources:y,trackInstances:u}},methods:{open:function(t){if(u){var e=t.to,n=t.from,s=t.passengers,o=t.order,i=void 0===o?1/0:o;if(e&&n&&s){var a={to:e,from:n,passengers:h(s),order:i},l=Object.keys(this.transports);-1===l.indexOf(e)&&r.set(this.transports,e,[]);var c=this.$_getTransportIndex(a),d=this.transports[e].slice(0);-1===c?d.push(a):d[c]=a,this.transports[e]=f(d,(function(t,e){return t.order-e.order}))}}},close:function(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=t.to,s=t.from;if(n&&(s||!1!==e)&&this.transports[n])if(e)this.transports[n]=[];else{var r=this.$_getTransportIndex(t);if(r>=0){var o=this.transports[n].slice(0);o.splice(r,1),this.transports[n]=o}}},registerTarget:function(t,e,n){u&&(this.trackInstances&&!n&&this.targets[t]&&console.warn("[portal-vue]: Target ".concat(t," already exists")),this.$set(this.targets,t,Object.freeze([e])))},unregisterTarget:function(t){this.$delete(this.targets,t)},registerSource:function(t,e,n){u&&(this.trackInstances&&!n&&this.sources[t]&&console.warn("[portal-vue]: source ".concat(t," already exists")),this.$set(this.sources,t,Object.freeze([e])))},unregisterSource:function(t){this.$delete(this.sources,t)},hasTarget:function(t){return!(!this.targets[t]||!this.targets[t][0])},hasSource:function(t){return!(!this.sources[t]||!this.sources[t][0])},hasContentFor:function(t){return!!this.transports[t]&&!!this.transports[t].length},$_getTransportIndex:function(t){var e=t.to,n=t.from;for(var s in this.transports[e])if(this.transports[e][s].from===n)return+s;return-1}}}),v=new b(m),T=1,S=r.extend({name:"portal",props:{disabled:{type:Boolean},name:{type:String,default:function(){return String(T++)}},order:{type:Number,default:0},slim:{type:Boolean},slotProps:{type:Object,default:function(){return{}}},tag:{type:String,default:"DIV"},to:{type:String,default:function(){return String(Math.round(1e7*Math.random()))}}},created:function(){var t=this;this.$nextTick((function(){v.registerSource(t.name,t)}))},mounted:function(){this.disabled||this.sendUpdate()},updated:function(){this.disabled?this.clear():this.sendUpdate()},beforeDestroy:function(){v.unregisterSource(this.name),this.clear()},watch:{to:function(t,e){e&&e!==t&&this.clear(e),this.sendUpdate()}},methods:{clear:function(t){var e={from:this.name,to:t||this.to};v.close(e)},normalizeSlots:function(){return this.$scopedSlots.default?[this.$scopedSlots.default]:this.$slots.default},normalizeOwnChildren:function(t){return"function"===typeof t?t(this.slotProps):t},sendUpdate:function(){var t=this.normalizeSlots();if(t){var e={from:this.name,to:this.to,passengers:i(t),order:this.order};v.open(e)}else this.clear()}},render:function(t){var e=this.$slots.default||this.$scopedSlots.default||[],n=this.tag;return e&&this.disabled?e.length<=1&&this.slim?this.normalizeOwnChildren(e)[0]:t(n,[this.normalizeOwnChildren(e)]):this.slim?t():t(n,{class:{"v-portal":!0},style:{display:"none"},key:"v-portal-placeholder"})}}),w=r.extend({name:"portalTarget",props:{multiple:{type:Boolean,default:!1},name:{type:String,required:!0},slim:{type:Boolean,default:!1},slotProps:{type:Object,default:function(){return{}}},tag:{type:String,default:"div"},transition:{type:[String,Object,Function]}},data:function(){return{transports:v.transports,firstRender:!0}},created:function(){var t=this;this.$nextTick((function(){v.registerTarget(t.name,t)}))},watch:{ownTransports:function(){this.$emit("change",this.children().length>0)},name:function(t,e){v.unregisterTarget(e),v.registerTarget(t,this)}},mounted:function(){var t=this;this.transition&&this.$nextTick((function(){t.firstRender=!1}))},beforeDestroy:function(){v.unregisterTarget(this.name)},computed:{ownTransports:function(){var t=this.transports[this.name]||[];return this.multiple?t:0===t.length?[]:[t[t.length-1]]},passengers:function(){return d(this.ownTransports,this.slotProps)}},methods:{children:function(){return 0!==this.passengers.length?this.passengers:this.$scopedSlots.default?this.$scopedSlots.default(this.slotProps):this.$slots.default||[]},noWrapper:function(){var t=this.slim&&!this.transition;return t&&this.children().length>1&&console.warn("[portal-vue]: PortalTarget with `slim` option received more than one child element."),t}},render:function(t){var e=this.noWrapper(),n=this.children(),s=this.transition||this.tag;return e?n[0]:this.slim&&!s?t():t(s,{props:{tag:this.transition&&this.tag?this.tag:void 0},class:{"vue-portal-target":!0}},n)}}),C=0,$=["disabled","name","order","slim","slotProps","tag","to"],k=["multiple","transition"],x=r.extend({name:"MountingPortal",inheritAttrs:!1,props:{append:{type:[Boolean,String]},bail:{type:Boolean},mountTo:{type:String,required:!0},disabled:{type:Boolean},name:{type:String,default:function(){return"mounted_"+String(C++)}},order:{type:Number,default:0},slim:{type:Boolean},slotProps:{type:Object,default:function(){return{}}},tag:{type:String,default:"DIV"},to:{type:String,default:function(){return String(Math.round(1e7*Math.random()))}},multiple:{type:Boolean,default:!1},targetSlim:{type:Boolean},targetSlotProps:{type:Object,default:function(){return{}}},targetTag:{type:String,default:"div"},transition:{type:[String,Object,Function]}},created:function(){if("undefined"!==typeof document){var t=document.querySelector(this.mountTo);if(t){var e=this.$props;if(v.targets[e.name])e.bail?console.warn("[portal-vue]: Target ".concat(e.name," is already mounted.\n Aborting because 'bail: true' is set")):this.portalTarget=v.targets[e.name];else{var n=e.append;if(n){var s="string"===typeof n?n:"DIV",r=document.createElement(s);t.appendChild(r),t=r}var o=p(this.$props,k);o.slim=this.targetSlim,o.tag=this.targetTag,o.slotProps=this.targetSlotProps,o.name=this.to,this.portalTarget=new w({el:t,parent:this.$parent||this,propsData:o})}}else console.error("[portal-vue]: Mount Point '".concat(this.mountTo,"' not found in document"))}},beforeDestroy:function(){var t=this.portalTarget;if(this.append){var e=t.$el;e.parentNode.removeChild(e)}t.$destroy()},render:function(t){if(!this.portalTarget)return console.warn("[portal-vue] Target wasn't mounted"),t();if(!this.$scopedSlots.manual){var e=p(this.$props,$);return t(S,{props:e,attrs:this.$attrs,on:this.$listeners,scopedSlots:this.$scopedSlots},this.$slots.default)}var n=this.$scopedSlots.manual({to:this.to});return Array.isArray(n)&&(n=n[0]),n||t()}});function I(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};t.component(e.portalName||"Portal",S),t.component(e.portalTargetName||"PortalTarget",w),t.component(e.MountingPortalName||"MountingPortal",x)}var P={install:I};e.h_=S,e.YC=w},8571:function(t,e){e["Z"]={state:{codeColors:null},reset(){this.state.codeColors=null},updateCodeColors(t){const e=t=>t?`rgba(${t.red}, ${t.green}, ${t.blue}, ${t.alpha})`:null;this.state.codeColors=Object.entries(t).reduce(((t,[n,s])=>({...t,[n]:e(s)})),{})}}},114:function(t,e,n){function s(t,e,n){return e in t?Object.defineProperty(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}):t[e]=n,t}n.d(e,{Z:function(){return o}});var r=n(7486);class o{constructor(t){s(this,"focusContainer",null),s(this,"tabTargets",[]),s(this,"firstTabTarget",null),s(this,"lastTabTarget",null),s(this,"lastFocusedElement",null),this.focusContainer=t,this.onFocus=this.onFocus.bind(this)}updateFocusContainer(t){this.focusContainer=t}start(){this.collectTabTargets(),this.firstTabTarget?this.focusContainer.contains(document.activeElement)&&r.ZP.isTabbableElement(document.activeElement)||this.firstTabTarget.focus():console.warn("There are no focusable elements. FocusTrap needs at least one."),this.lastFocusedElement=document.activeElement,document.addEventListener("focus",this.onFocus,!0)}stop(){document.removeEventListener("focus",this.onFocus,!0)}collectTabTargets(){this.tabTargets=r.ZP.getTabbableElements(this.focusContainer),this.firstTabTarget=this.tabTargets[0],this.lastTabTarget=this.tabTargets[this.tabTargets.length-1]}onFocus(t){if(this.focusContainer.contains(t.target))this.lastFocusedElement=t.target;else{if(t.preventDefault(),this.collectTabTargets(),this.lastFocusedElement===this.lastTabTarget||!this.lastFocusedElement||!document.contains(this.lastFocusedElement))return this.firstTabTarget.focus(),void(this.lastFocusedElement=this.firstTabTarget);this.lastFocusedElement===this.firstTabTarget&&(this.lastTabTarget.focus(),this.lastFocusedElement=this.lastTabTarget)}}destroy(){this.stop(),this.focusContainer=null,this.tabTargets=[],this.firstTabTarget=null,this.lastTabTarget=null,this.lastFocusedElement=null}}}}]); \ No newline at end of file diff --git a/docs/js/866.f57382ad.js b/docs/js/866.f57382ad.js new file mode 100644 index 0000000..920942d --- /dev/null +++ b/docs/js/866.f57382ad.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[866],{4655:function(e,t,n){"use strict";n.d(t,{Z:function(){return F}});var r,i,s=function(){var e=this,t=e._self._c;return t("div",{staticClass:"asset"},[t(e.assetComponent,e._g(e._b({tag:"component"},"component",e.assetProps,!1),e.assetListeners))],1)},a=[],o=n(6769),l=function(){var e=this,t=e._self._c;return t("ConditionalWrapper",{ref:"wrapper",attrs:{tag:e.DeviceFrameComponent,"should-wrap":!!e.deviceFrame,device:e.deviceFrame}},[t("div",[t("video",{key:e.videoAttributes.url,ref:"video",attrs:{id:e.id,controls:e.showsDefaultControls,"data-orientation":e.orientation,autoplay:e.autoplays,poster:e.normalisedPosterPath,width:e.optimalWidth,"aria-roledescription":e.$t("video.title"),"aria-labelledby":e.showsDefaultControls&&e.alt?e.altTextId:null,playsinline:""},domProps:{muted:e.muted},on:{loadedmetadata:e.setOrientation,playing:function(t){return e.$emit("playing")},pause:function(t){return e.$emit("pause")},ended:function(t){return e.$emit("ended")}}},[t("source",{attrs:{src:e.normalizePath(e.videoAttributes.url)}})]),e.alt?t("span",{attrs:{id:e.altTextId,hidden:""}},[e._v(" "+e._s(e.$t("video.description",{alt:e.alt}))+" ")]):e._e()])])},c=[],u=n(5947),d=n(4030),A=n(9804),p={functional:!0,name:"ConditionalWrapper",props:{tag:[Object,String],shouldWrap:Boolean},render(e,t){return t.props.shouldWrap?e(t.props.tag,t.data,t.children):t.children}},h=p,g=n(1001),f=(0,g.Z)(h,r,i,!1,null,null,null),m=f.exports,v=n(889),b={name:"VideoAsset",components:{ConditionalWrapper:m},props:{variants:{type:Array,required:!0},showsDefaultControls:{type:Boolean,default:()=>!1},autoplays:{type:Boolean,default:()=>!1},posterVariants:{type:Array,required:!1,default:()=>[]},muted:{type:Boolean,default:!1},deviceFrame:{type:String,required:!1},alt:{type:String,required:!1},id:{type:String,required:!0}},data:()=>({appState:d["default"].state,optimalWidth:null,orientation:null}),computed:{DeviceFrameComponent:()=>v.Z,preferredColorScheme:({appState:e})=>e.preferredColorScheme,systemColorScheme:({appState:e})=>e.systemColorScheme,altTextId:({id:e})=>`${e}-alt`,userPrefersDark:({preferredColorScheme:e,systemColorScheme:t})=>e===A.Z.dark||e===A.Z.auto&&t===A.Z.dark,shouldShowDarkVariant:({darkVideoVariantAttributes:e,userPrefersDark:t})=>e&&t,defaultVideoAttributes(){return this.videoVariantsGroupedByAppearance.light[0]||this.darkVideoVariantAttributes||{}},darkVideoVariantAttributes(){return this.videoVariantsGroupedByAppearance.dark[0]},videoVariantsGroupedByAppearance(){return(0,u.XV)(this.variants)},posterVariantsGroupedByAppearance(){const{light:e,dark:t}=(0,u.XV)(this.posterVariants);return{light:(0,u.u)(e),dark:(0,u.u)(t)}},defaultPosterAttributes:({posterVariantsGroupedByAppearance:e,userPrefersDark:t})=>t&&e.dark.length?e.dark[0]:e.light[0]||{},normalisedPosterPath:({defaultPosterAttributes:e})=>(0,u.AH)(e.src),videoAttributes:({darkVideoVariantAttributes:e,defaultVideoAttributes:t,shouldShowDarkVariant:n})=>n?e:t},watch:{normalisedPosterPath:{immediate:!0,handler:"getPosterDimensions"}},methods:{normalizePath:u.AH,async getPosterDimensions(e){if(!e)return void(this.optimalWidth=null);const{density:t}=this.defaultPosterAttributes,n=parseInt(t.match(/\d+/)[0],10),{width:r}=await(0,u.RY)(e);this.optimalWidth=r/n},setOrientation(){const{videoWidth:e,videoHeight:t}=this.$refs.video;this.orientation=(0,u.T8)(e,t)}}},y=b,C=(0,g.Z)(y,l,c,!1,null,null,null),I=C.exports,w=function(){var e=this,t=e._self._c;return t("div",{staticClass:"video-replay-container",attrs:{role:"group","aria-roledescription":e.$t("video.title"),"aria-labelledby":e.showsDefaultControls?null:e.ariaLabelledByContainer}},[t("span",{attrs:{id:`${e.id}-custom-controls`,hidden:""}},[e._v(" "+e._s(e.$t("video.custom-controls"))+" ")]),t("VideoAsset",{ref:"asset",attrs:{variants:e.variants,autoplays:e.autoplays,showsDefaultControls:e.showsDefaultControls,muted:e.muted,posterVariants:e.posterVariants,deviceFrame:e.deviceFrame,alt:e.alt,id:e.id},on:{pause:e.onPause,playing:e.onVideoPlaying,ended:e.onVideoEnd}}),e.showsDefaultControls?e._e():t("a",{staticClass:"control-button",attrs:{href:"#","aria-controls":e.id},on:{click:function(t){return t.preventDefault(),e.togglePlayStatus.apply(null,arguments)}}},[e._v(" "+e._s(e.text)+" "),e.videoEnded?t("InlineReplayIcon",{staticClass:"control-icon icon-inline"}):e.isPlaying?t("PauseIcon",{staticClass:"control-icon icon-inline"}):t("PlayIcon",{staticClass:"control-icon icon-inline"})],1)],1)},E=[],B=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"inline-replay-icon",attrs:{viewBox:"0 0 14 14",themeId:"inline-replay"}},[t("path",{attrs:{d:"M2.254 10.201c-1.633-2.613-0.838-6.056 1.775-7.689 2.551-1.594 5.892-0.875 7.569 1.592l0.12 0.184-0.848 0.53c-1.34-2.145-4.166-2.797-6.311-1.457s-2.797 4.166-1.457 6.311 4.166 2.797 6.311 1.457c1.006-0.629 1.71-1.603 2.003-2.723l0.056-0.242 0.98 0.201c-0.305 1.487-1.197 2.792-2.51 3.612-2.613 1.633-6.056 0.838-7.689-1.775z"}}),t("path",{attrs:{d:"M10.76 1.355l0.984-0.18 0.851 4.651-4.56-1.196 0.254-0.967 3.040 0.796z"}})])},x=[],k=n(9742),_={name:"InlineReplayIcon",components:{SVGIcon:k.Z}},S=_,T=(0,g.Z)(S,B,x,!1,null,null,null),L=T.exports,P=n(6698),Q=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"pause-icon",attrs:{viewBox:"0 0 14 14",themeId:"pause"}},[t("path",{attrs:{d:"M5 4h1v6h-1z"}}),t("path",{attrs:{d:"M8 4h1v6h-1z"}}),t("path",{attrs:{d:"M7 0.5c-3.6 0-6.5 2.9-6.5 6.5s2.9 6.5 6.5 6.5 6.5-2.9 6.5-6.5-2.9-6.5-6.5-6.5zM7 12.5c-3 0-5.5-2.5-5.5-5.5s2.5-5.5 5.5-5.5 5.5 2.5 5.5 5.5-2.5 5.5-5.5 5.5z"}})])},M=[],Z={name:"PauseIcon",components:{SVGIcon:k.Z}},D=Z,O=(0,g.Z)(D,Q,M,!1,null,null,null),N=O.exports,R={name:"ReplayableVideoAsset",components:{PauseIcon:N,PlayIcon:P.Z,InlineReplayIcon:L,VideoAsset:I},props:{variants:{type:Array,required:!0},alt:{type:String,required:!1},id:{type:String,required:!0},showsDefaultControls:{type:Boolean,default:()=>!1},autoplays:{type:Boolean,default:()=>!1},muted:{type:Boolean,default:!1},posterVariants:{type:Array,default:()=>[]},deviceFrame:{type:String,required:!1}},computed:{text(){return this.videoEnded?this.$t("video.replay"):this.isPlaying?this.$t("video.pause"):this.$t("video.play")},ariaLabelledByContainer:({id:e,alt:t})=>t?`${e}-custom-controls ${e}-alt`:`${e}-custom-controls`},data(){return{isPlaying:!1,videoEnded:!1}},methods:{async togglePlayStatus(){const e=this.$refs.asset.$refs.video;e&&(this.isPlaying&&!this.videoEnded?await e.pause():await e.play())},onVideoEnd(){this.isPlaying=!1,this.videoEnded=!0},onVideoPlaying(){const{video:e}=this.$refs.asset.$refs;this.isPlaying=!e.paused,this.videoEnded=e.ended},onPause(){const{video:e}=this.$refs.asset.$refs;!this.showsDefaultControls&&this.isPlaying&&(this.isPlaying=!1),this.videoEnded=e.ended}}},j=R,G=(0,g.Z)(j,w,E,!1,null,"3fb37a97",null),V=G.exports,$=n(5953);const z={video:"video",image:"image"};var q={name:"Asset",components:{ImageAsset:o.Z,VideoAsset:I},constants:{AssetTypes:z},mixins:[$.Z],props:{identifier:{type:String,required:!0},showsReplayButton:{type:Boolean,default:()=>!0},showsVideoControls:{type:Boolean,default:()=>!1},videoAutoplays:{type:Boolean,default:()=>!1},videoMuted:{type:Boolean,default:!1},deviceFrame:{type:String,required:!1}},computed:{rawAsset(){return this.references[this.identifier]||{}},isRawAssetVideo:({rawAsset:e})=>e.type===z.video,videoPoster(){return this.isRawAssetVideo&&this.references[this.rawAsset.poster]},asset(){return this.isRawAssetVideo&&this.prefersReducedMotion&&this.videoPoster||this.rawAsset},assetComponent(){switch(this.asset.type){case z.image:return o.Z;case z.video:return this.showsReplayButton?V:I;default:return}},prefersReducedMotion(){return window.matchMedia("(prefers-reduced-motion)").matches},assetProps(){return{[z.image]:this.imageProps,[z.video]:this.videoProps}[this.asset.type]},imageProps(){return{alt:this.asset.alt,variants:this.asset.variants}},videoProps(){return{variants:this.asset.variants,showsDefaultControls:this.showsVideoControls,muted:this.videoMuted,autoplays:!this.prefersReducedMotion&&this.videoAutoplays,posterVariants:this.videoPoster?this.videoPoster.variants:[],deviceFrame:this.deviceFrame,alt:this.asset.alt,id:this.identifier}},assetListeners(){return{[z.image]:null,[z.video]:{ended:()=>this.$emit("videoEnded")}}[this.asset.type]}}},H=q,W=(0,g.Z)(H,s,a,!1,null,"6ab0b718",null),F=W.exports},7188:function(e,t,n){"use strict";n.d(t,{default:function(){return h}});var r=n(5381);const i=e=>e?`(max-width: ${e}px)`:"",s=e=>e?`(min-width: ${e}px)`:"";function a({minWidth:e,maxWidth:t}){return["only screen",s(e),i(t)].filter(Boolean).join(" and ")}function o({maxWidth:e,minWidth:t}){return window.matchMedia(a({minWidth:t,maxWidth:e}))}var l,c,u={name:"BreakpointEmitter",constants:{BreakpointAttributes:r.kB,BreakpointName:r.L3,BreakpointScopes:r.lU},props:{scope:{type:String,default:()=>r.lU["default"],validator:e=>e in r.lU}},render(){return this.$scopedSlots.default?this.$scopedSlots.default({matchingBreakpoint:this.matchingBreakpoint}):null},data:()=>({matchingBreakpoint:null}),methods:{initMediaQuery(e,t){const n=o(t),r=t=>this.handleMediaQueryChange(t,e);n.addListener(r),this.$once("hook:beforeDestroy",(()=>{n.removeListener(r)})),r(n)},handleMediaQueryChange(e,t){e.matches&&(this.matchingBreakpoint=t,this.$emit("change",t))}},mounted(){const e=r.kB[this.scope]||{};Object.entries(e).forEach((([e,t])=>{this.initMediaQuery(e,t)}))}},d=u,A=n(1001),p=(0,A.Z)(d,l,c,!1,null,null,null),h=p.exports},5281:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=function(){var e=this,t=e._self._c;return t(e.resolvedComponent,e._b({tag:"component",staticClass:"button-cta",class:{"is-dark":e.isDark}},"component",e.componentProps,!1),[e._t("default")],2)},i=[],s=n(9882),a={name:"ButtonLink",components:{Reference:s.Z},props:{url:{type:String,required:!1},isDark:{type:Boolean,default:!1}},computed:{resolvedComponent:({url:e})=>e?s.Z:"button",componentProps:({url:e})=>e?{url:e}:{}}},o=a,l=n(1001),c=(0,l.Z)(o,r,i,!1,null,"c9c81868",null),u=c.exports},7605:function(e,t,n){"use strict";n.d(t,{Z:function(){return d}});var r=function(){var e=this,t=e._self._c;return e.action?t("DestinationDataProvider",{attrs:{destination:e.action},scopedSlots:e._u([{key:"default",fn:function({url:n,title:r}){return[t("ButtonLink",{attrs:{url:n,isDark:e.isDark}},[e._v(" "+e._s(r)+" ")])]}}],null,!1,710653997)}):e._e()},i=[],s=n(5281),a=n(1295),o={name:"CallToActionButton",components:{DestinationDataProvider:a.Z,ButtonLink:s.Z},props:{action:{type:Object,required:!0},isDark:{type:Boolean,default:!1}}},l=o,c=n(1001),u=(0,c.Z)(l,r,i,!1,null,null,null),d=u.exports},3917:function(e,t,n){"use strict";n.d(t,{Z:function(){return c}});var r=function(){var e=this,t=e._self._c;return t("code",{attrs:{tabindex:"0","data-before-code":e.$t("accessibility.code.start"),"data-after-code":e.$t("accessibility.code.end")}},[e._t("default")],2)},i=[],s={name:"CodeBlock"},a=s,o=n(1001),l=(0,o.Z)(a,r,i,!1,null,"08295b2f",null),c=l.exports},9519:function(e,t,n){"use strict";n.r(t),n.d(t,{BlockType:function(){return _t},default:function(){return Rt}});var r=n(5953),i=n(7587),s=n(5996),a=n(8039),o=n(2020),l=function(){var e=this,t=e._self._c;return t("div",{staticClass:"DictionaryExample"},[e._t("default"),t("CollapsibleCodeListing",{attrs:{content:e.example.content,showLineNumbers:""}})],2)},c=[],u=function(){var e=this,t=e._self._c;return t("div",{staticClass:"collapsible-code-listing",class:{"single-line":1===e.content[0].code.length}},[t("pre",[t("CodeBlock",e._l(this.content,(function(n,r){return t("div",{key:r,class:["container-general",{collapsible:!0===n.collapsible},{collapsed:!0===n.collapsible&&e.collapsed}]},e._l(n.code,(function(n,r){return t("div",{key:r,staticClass:"code-line-container"},[e._v("\n "),t("div",{directives:[{name:"show",rawName:"v-show",value:e.showLineNumbers,expression:"showLineNumbers"}],staticClass:"code-number"}),e._v("\n "),t("div",{staticClass:"code-line"},[e._v(e._s(n))]),e._v("\n ")])})),0)})),0)],1)])},d=[],A=n(3917),p={name:"CollapsibleCodeListing",components:{CodeBlock:A.Z},props:{collapsed:{type:Boolean,required:!1},content:{type:Array,required:!0},showLineNumbers:{type:Boolean,default:()=>!0}}},h=p,g=n(1001),f=(0,g.Z)(h,u,d,!1,null,"25a17a0e",null),m=f.exports,v={name:"DictionaryExample",components:{CollapsibleCodeListing:m},props:{example:{type:Object,required:!0}}},b=v,y=(0,g.Z)(b,l,c,!1,null,null,null),C=y.exports,I=function(){var e=this,t=e._self._c;return t("Row",{staticClass:"endpoint-example"},[t("Column",{staticClass:"example-code"},[e._t("default"),t("Tabnav",{model:{value:e.currentTab,callback:function(t){e.currentTab=t},expression:"currentTab"}},[t("TabnavItem",{attrs:{value:e.Tab.request}},[e._v(e._s(e.$t("tab.request")))]),t("TabnavItem",{attrs:{value:e.Tab.response}},[e._v(e._s(e.$t("tab.response")))])],1),t("div",{staticClass:"output"},[e.isCurrent(e.Tab.request)?t("div",{staticClass:"code"},[t("CollapsibleCodeListing",e._b({attrs:{collapsed:e.isCollapsed,showLineNumbers:""}},"CollapsibleCodeListing",e.request,!1))],1):e._e(),e.isCurrent(e.Tab.response)?t("div",{staticClass:"code"},[t("CollapsibleCodeListing",e._b({attrs:{collapsed:e.isCollapsed,showLineNumbers:""}},"CollapsibleCodeListing",e.response,!1))],1):e._e()]),e.isCollapsible?t("div",{staticClass:"controls"},[e.isCollapsed?t("a",{staticClass:"toggle",attrs:{href:"#"},on:{click:function(t){return t.preventDefault(),e.showMore.apply(null,arguments)}}},[t("InlinePlusCircleSolidIcon",{staticClass:"control-icon icon-inline"}),e._v(" "+e._s(e.$t("more"))+" ")],1):t("a",{staticClass:"toggle",attrs:{href:"#"},on:{click:function(t){return t.preventDefault(),e.showLess.apply(null,arguments)}}},[t("InlineMinusCircleSolidIcon",{staticClass:"control-icon icon-inline"}),e._v(" "+e._s(e.$t("less"))+" ")],1)]):e._e()],2)],1)},w=[],E=n(9649),B=n(1576),x=function(){var e=this,t=e._self._c;return t("nav",{staticClass:"tabnav",class:{[`tabnav--${e.position}`]:e.position,"tabnav--vertical":e.vertical}},[t("ul",{staticClass:"tabnav-items"},[e._t("default")],2)])},k=[];const _="tabnavData";var S={name:"Tabnav",constants:{ProvideKey:_},provide(){const e={selectTab:this.selectTab};return Object.defineProperty(e,"activeTab",{enumerable:!0,get:()=>this.value}),{[_]:e}},props:{position:{type:String,required:!1,validator:e=>new Set(["start","center","end"]).has(e)},vertical:{type:Boolean,default:!1},value:{type:[String,Number],required:!0}},methods:{selectTab(e){this.$emit("input",e)}}},T=S,L=(0,g.Z)(T,x,k,!1,null,"5572fe1d",null),P=L.exports,Q=function(){var e=this,t=e._self._c;return t("li",{staticClass:"tabnav-item"},[t("a",{staticClass:"tabnav-link",class:{active:e.isActive},attrs:{href:"#","aria-current":e.isActive?"true":"false"},on:{click:function(t){return t.preventDefault(),e.tabnavData.selectTab(e.value)}}},[e._t("default")],2)])},M=[],Z={name:"TabnavItem",inject:{tabnavData:{default:{activeTab:null,selectTab:()=>{}}}},props:{value:{type:[String,Number],default:null}},computed:{isActive({tabnavData:e,value:t}){return e.activeTab===t}}},D=Z,O=(0,g.Z)(D,Q,M,!1,null,"6aa9882a",null),N=O.exports,R=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"inline-plus-circle-solid-icon",attrs:{viewBox:"0 0 14 14",themeId:"inline-plus-circle-solid"}},[t("path",{attrs:{d:"M7.005 0.5h-0.008c-1.791 0.004-3.412 0.729-4.589 1.9l0-0c-1.179 1.177-1.908 2.803-1.908 4.6 0 3.59 2.91 6.5 6.5 6.5s6.5-2.91 6.5-6.5c0-3.587-2.906-6.496-6.492-6.5h-0zM4.005 7.52v-1h2.5v-2.51h1v2.51h2.5v1h-2.501v2.49h-1v-2.49z"}})])},j=[],G=n(9742),V={name:"InlinePlusCircleSolidIcon",components:{SVGIcon:G.Z}},$=V,z=(0,g.Z)($,R,j,!1,null,null,null),q=z.exports,H=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"inline-minus-circle-solid-icon",attrs:{viewBox:"0 0 14 14",themeId:"inline-minus-circle-solid"}},[t("path",{attrs:{d:"m6.98999129.48999129c3.58985091 0 6.50000001 2.91014913 6.50000001 6.5 0 3.58985091-2.9101491 6.50000001-6.50000001 6.50000001-3.58985087 0-6.5-2.9101491-6.5-6.50000001 0-3.58985087 2.91014913-6.5 6.5-6.5zm3 6.02001742h-6v1h6z","fill-rule":"evenodd"}})])},W=[],F={name:"InlineMinusCircleSolidIcon",components:{SVGIcon:G.Z}},U=F,Y=(0,g.Z)(U,H,W,!1,null,null,null),X=Y.exports;const K={request:"Request",response:"Response"};var J={name:"EndpointExample",components:{InlineMinusCircleSolidIcon:X,InlinePlusCircleSolidIcon:q,TabnavItem:N,Tabnav:P,CollapsibleCodeListing:m,Row:E.Z,Column:B.Z},constants:{Tab:K},props:{request:{type:Object,required:!0},response:{type:Object,required:!0}},data(){return{isCollapsed:!0,currentTab:K.request}},computed:{Tab:()=>K,isCollapsible:({response:e,request:t,currentTab:n})=>{const r={[K.request]:t.content,[K.response]:e.content}[n]||[];return r.some((({collapsible:e})=>e))}},methods:{isCurrent(e){return this.currentTab===e},showMore(){this.isCollapsed=!1},showLess(){this.isCollapsed=!0}}},ee=J,te=(0,g.Z)(ee,I,w,!1,null,"c84e62a6",null),ne=te.exports,re=function(){var e=this,t=e._self._c;return t("figure",{attrs:{id:e.anchor}},[e._t("default")],2)},ie=[],se={name:"Figure",props:{anchor:{type:String,required:!1}}},ae=se,oe=(0,g.Z)(ae,re,ie,!1,null,null,null),le=oe.exports,ce=function(){var e=this,t=e._self._c;return t(e.tag,{tag:"component",staticClass:"caption",class:{trailing:e.trailing}},[e.title?[t("strong",[e._v(e._s(e.title))]),e._v(" "),e._t("default")]:[e._t("default")]],2)},ue=[];const de={caption:"caption",figcaption:"figcaption"},Ae={leading:"leading",trailing:"trailing"};var pe={name:"Caption",constants:{CaptionPosition:Ae,CaptionTag:de},props:{title:{type:String,required:!1},tag:{type:String,required:!0,validator:e=>Object.hasOwnProperty.call(de,e)},position:{type:String,default:()=>Ae.leading,validator:e=>Object.hasOwnProperty.call(Ae,e)}},computed:{trailing:({position:e})=>e===Ae.trailing}},he=pe,ge=(0,g.Z)(he,ce,ue,!1,null,"869c6f6e",null),fe=ge.exports,me=function(){var e=this,t=e._self._c;return t("ImageAsset",{attrs:{alt:e.alt,variants:e.variants}})},ve=[],be=n(6769),ye={name:"InlineImage",components:{ImageAsset:be.Z},props:{alt:{type:String,default:""},variants:{type:Array,required:!0}}},Ce=ye,Ie=(0,g.Z)(Ce,me,ve,!1,null,"bf997940",null),we=Ie.exports,Ee=n(9882),Be=function(){var e=this,t=e._self._c;return t("div",{staticClass:"table-wrapper"},[t("table",{class:{spanned:e.spanned}},[e._t("default")],2)])},xe=[],ke={name:"Table",props:{spanned:{type:Boolean,default:!1}}},_e=ke,Se=(0,g.Z)(_e,Be,xe,!1,null,"f3322390",null),Te=Se.exports,Le=function(){var e=this,t=e._self._c;return t("s",{attrs:{"data-before-text":e.$t("accessibility.strike.start"),"data-after-text":e.$t("accessibility.strike.end")}},[e._t("default")],2)},Pe=[],Qe={name:"StrikeThrough"},Me=Qe,Ze=(0,g.Z)(Me,Le,Pe,!1,null,"7fc51673",null),De=Ze.exports,Oe=function(){var e=this,t=e._self._c;return t("small",[e._t("default")],2)},Ne=[],Re={name:"Small"},je=Re,Ge=(0,g.Z)(je,Oe,Ne,!1,null,"77035f61",null),Ve=Ge.exports,$e=function(){var e=this,t=e._self._c;return t("Asset",{attrs:{identifier:e.identifier,deviceFrame:e.deviceFrame}})},ze=[],qe=n(4655),He={name:"BlockVideo",components:{Asset:qe.Z},props:{identifier:{type:String,required:!0},deviceFrame:{type:String,required:!1}}},We=He,Fe=(0,g.Z)(We,$e,ze,!1,null,"4f18340d",null),Ue=Fe.exports,Ye=n(3938),Xe=n(3002),Ke=function(){var e=this,t=e._self._c;return t("div",{staticClass:"TabNavigator",class:[{"tabs--vertical":e.vertical}]},[t("Tabnav",e._b({model:{value:e.currentTitle,callback:function(t){e.currentTitle=t},expression:"currentTitle"}},"Tabnav",{position:e.position,vertical:e.vertical},!1),e._l(e.titles,(function(n){return t("TabnavItem",{key:n,attrs:{value:n}},[e._v(" "+e._s(n)+" ")])})),1),t("div",{staticClass:"tabs-content"},[t("div",{staticClass:"tabs-content-container"},[t("transition-group",{attrs:{name:"fade"}},[e._l(e.titles,(function(n){return[t("div",{directives:[{name:"show",rawName:"v-show",value:n===e.currentTitle,expression:"title === currentTitle"}],key:n,staticClass:"tab-container",class:{active:n===e.currentTitle}},[e._t(n)],2)]}))],2)],1)])],1)},Je=[],et={name:"TabNavigator",components:{TabnavItem:N,Tabnav:P},props:{vertical:{type:Boolean,default:!1},position:{type:String,default:"start",validator:e=>new Set(["start","center","end"]).has(e)},titles:{type:Array,required:!0,default:()=>[]}},data(){return{currentTitle:this.titles[0]}},watch:{titles(e,t){if(e.length!t.includes(e)));this.currentTitle=n||this.currentTitle}}}},tt=et,nt=(0,g.Z)(tt,Ke,Je,!1,null,"e671a734",null),rt=nt.exports,it=function(){var e=this,t=e._self._c;return t("ul",{staticClass:"tasklist"},e._l(e.tasks,(function(n,r){return t("li",{key:r},[e.showCheckbox(n)?t("input",{attrs:{type:"checkbox",disabled:""},domProps:{checked:n.checked}}):e._e(),e._t("task",null,{task:n})],2)})),0)},st=[];const at="checked",ot=e=>Object.hasOwnProperty.call(e,at);var lt={name:"TaskList",props:{tasks:{required:!0,type:Array,validator:e=>e.some(ot)}},methods:{showCheckbox:ot}},ct=lt,ut=(0,g.Z)(ct,it,st,!1,null,"6a56a858",null),dt=ut.exports,At=function(){var e=this,t=e._self._c;return e.isListStyle?t("div",{staticClass:"links-block"},e._l(e.items,(function(e){return t("TopicsLinkBlock",{key:e.identifier,staticClass:"topic-link-block",attrs:{topic:e}})})),1):t("TopicsLinkCardGrid",{staticClass:"links-block",attrs:{items:e.items,"topic-style":e.blockStyle}})},pt=[],ht=n(1105),gt=n(3946),ft={name:"LinksBlock",mixins:[r.Z],components:{TopicsLinkBlock:()=>Promise.all([n.e(104),n.e(773)]).then(n.bind(n,8104)),TopicsLinkCardGrid:ht.Z},props:{identifiers:{type:Array,required:!0},blockStyle:{type:String,default:gt.o.compactGrid}},computed:{isListStyle:({blockStyle:e})=>e===gt.o.list,items:({identifiers:e,references:t})=>e.reduce(((e,n)=>t[n]?e.concat(t[n]):e),[])}},mt=ft,vt=(0,g.Z)(mt,At,pt,!1,null,"b1a75c1c",null),bt=vt.exports,yt=n(889),Ct=function(){var e=this,t=e._self._c;return t("hr",{staticClass:"thematic-break"})},It=[],wt={},Et=(0,g.Z)(wt,Ct,It,!1,null,"62d2922a",null),Bt=Et.exports;const{CaptionPosition:xt,CaptionTag:kt}=fe.constants,_t={aside:"aside",codeListing:"codeListing",endpointExample:"endpointExample",heading:"heading",orderedList:"orderedList",paragraph:"paragraph",table:"table",termList:"termList",unorderedList:"unorderedList",dictionaryExample:"dictionaryExample",small:"small",video:"video",row:"row",tabNavigator:"tabNavigator",links:"links",thematicBreak:"thematicBreak"},St={codeVoice:"codeVoice",emphasis:"emphasis",image:"image",inlineHead:"inlineHead",link:"link",newTerm:"newTerm",reference:"reference",strong:"strong",text:"text",superscript:"superscript",subscript:"subscript",strikethrough:"strikethrough"},Tt={both:"both",column:"column",none:"none",row:"row"},Lt={left:"left",right:"right",center:"center",unset:"unset"},Pt=7;function Qt(e,t){const n=n=>n.map(Qt(e,t)),r=t=>t.map((t=>e("li",{},n(t.content)))),l=(t,r,i,s,a,o,l)=>{const{colspan:c,rowspan:u}=o[`${a}_${s}`]||{};if(0===c||0===u)return null;const d=l[s]||Lt.unset;let A=null;return d!==Lt.unset&&(A=`${d}-cell`),e(t,{attrs:{...r,colspan:c,rowspan:u},class:A},n(i))},c=(t,n=Tt.none,r={},i=[])=>{switch(n){case Tt.both:{const[n,...s]=t;return[e("thead",{},[e("tr",{},n.map(((e,t)=>l("th",{scope:"col"},e,t,0,r,i))))]),e("tbody",{},s.map((([t,...n],s)=>e("tr",{},[l("th",{scope:"row"},t,0,s+1,r,i),...n.map(((e,t)=>l("td",{},e,t+1,s+1,r,i)))]))))]}case Tt.column:return[e("tbody",{},t.map((([t,...n],s)=>e("tr",{},[l("th",{scope:"row"},t,0,s,r,i),...n.map(((e,t)=>l("td",{},e,t+1,s,r,i)))]))))];case Tt.row:{const[n,...s]=t;return[e("thead",{},[e("tr",{},n.map(((e,t)=>l("th",{scope:"col"},e,t,0,r,i))))]),e("tbody",{},s.map(((t,n)=>e("tr",{},t.map(((e,t)=>l("td",{},e,t,n+1,r,i)))))))]}default:return[e("tbody",{},t.map(((t,n)=>e("tr",{},t.map(((e,t)=>l("td",{},e,t,n,r,i)))))))]}},u=({metadata:{abstract:t=[],anchor:r,title:i,...s},...a})=>{const o={...a,metadata:s},l=[n([o])];if(i&&t.length||t.length){const r=i?xt.leading:xt.trailing,s=r===xt.trailing?1:0,a=kt.figcaption;l.splice(s,0,e(fe,{props:{title:i,position:r,tag:a}},n(t)))}return e(le,{props:{anchor:r}},l)},d=({metadata:{deviceFrame:t},...r})=>e(yt.Z,{props:{device:t}},n([r]));return function(l){switch(l.type){case _t.aside:{const t={kind:l.style,name:l.name};return e(i.Z,{props:t},n(l.content))}case _t.codeListing:{if(l.metadata&&l.metadata.anchor)return u(l);const t={syntax:l.syntax,fileType:l.fileType,content:l.code,showLineNumbers:l.showLineNumbers};return e(s.Z,{props:t})}case _t.endpointExample:{const t={request:l.request,response:l.response};return e(ne,{props:t},n(l.summary||[]))}case _t.heading:{const t={anchor:l.anchor,level:l.level};return e(a.Z,{props:t},l.text)}case _t.orderedList:return e("ol",{attrs:{start:l.start}},r(l.items));case _t.paragraph:{const t=1===l.inlineContent.length&&l.inlineContent[0].type===St.image,r=t?{class:["inline-image-container"]}:{};return e("p",r,n(l.inlineContent))}case _t.table:{const t=c(l.rows,l.header,l.extendedData,l.alignments);if(l.metadata&&l.metadata.abstract){const{title:r}=l.metadata,i=r?xt.leading:xt.trailing,s=kt.caption;t.unshift(e(fe,{props:{title:r,position:i,tag:s}},n(l.metadata.abstract)))}return e(Te,{attrs:{id:l.metadata&&l.metadata.anchor},props:{spanned:!!l.extendedData}},t)}case _t.termList:return e("dl",{},l.items.map((({term:t,definition:r})=>[e("dt",{},n(t.inlineContent)),e("dd",{},n(r.content))])));case _t.unorderedList:{const t=e=>dt.props.tasks.validator(e.items);return t(l)?e(dt,{props:{tasks:l.items},scopedSlots:{task:e=>n(e.task.content)}}):e("ul",{},r(l.items))}case _t.dictionaryExample:{const t={example:l.example};return e(C,{props:t},n(l.summary||[]))}case _t.small:return e("p",{},[e(Ve,{},n(l.inlineContent))]);case _t.video:{if(l.metadata&&l.metadata.abstract)return u(l);if(!t[l.identifier])return null;const{deviceFrame:n}=l.metadata||{};return e(Ue,{props:{identifier:l.identifier,deviceFrame:n}})}case _t.row:{const t=l.numberOfColumns?{large:l.numberOfColumns}:void 0;return e(Xe.Z,{props:{columns:t}},l.columns.map((t=>e(Ye.Z,{props:{span:t.size}},n(t.content)))))}case _t.tabNavigator:{const t=l.tabs.length>Pt,r=l.tabs.map((e=>e.title)),i=l.tabs.reduce(((e,t)=>({...e,[t.title]:()=>n(t.content)})),{});return e(rt,{props:{titles:r,vertical:t},scopedSlots:i})}case _t.links:return e(bt,{props:{blockStyle:l.style,identifiers:l.items}});case _t.thematicBreak:return e(Bt);case St.codeVoice:return e(o.Z,{},l.code);case St.emphasis:case St.newTerm:return e("em",n(l.inlineContent));case St.image:{if(l.metadata&&(l.metadata.anchor||l.metadata.abstract))return u(l);const n=t[l.identifier];return n?l.metadata&&l.metadata.deviceFrame?d(l):e(we,{props:{alt:n.alt,variants:n.variants}}):null}case St.link:return e("a",{attrs:{href:l.destination},class:"inline-link"},l.title);case St.reference:{const r=t[l.identifier];if(!r)return null;const i=l.overridingTitleInlineContent||r.titleInlineContent,s=l.overridingTitle||r.title;return e(Ee.Z,{props:{url:r.url,kind:r.kind,role:r.role,isActive:l.isActive,ideTitle:r.ideTitle,titleStyle:r.titleStyle,hasInlineFormatting:!!i},class:"inline-link"},i?n(i):s)}case St.strong:case St.inlineHead:return e("strong",n(l.inlineContent));case St.text:return"\n"===l.text?e("br"):l.text;case St.superscript:return e("sup",n(l.inlineContent));case St.subscript:return e("sub",n(l.inlineContent));case St.strikethrough:return e(De,n(l.inlineContent));default:return null}}}var Mt,Zt,Dt={name:"ContentNode",constants:{TableHeaderStyle:Tt,TableColumnAlignments:Lt},mixins:[r.Z],render:function(e){return e(this.tag,{class:"content"},this.content.map(Qt(e,this.references),this))},props:{content:{type:Array,required:!0},tag:{type:String,default:()=>"div"}},methods:{map(e){function t(n=[]){return n.map((n=>{switch(n.type){case _t.aside:return e({...n,content:t(n.content)});case _t.dictionaryExample:return e({...n,summary:t(n.summary)});case _t.paragraph:case St.emphasis:case St.strong:case St.inlineHead:case St.superscript:case St.subscript:case St.strikethrough:case St.newTerm:return e({...n,inlineContent:t(n.inlineContent)});case _t.orderedList:case _t.unorderedList:return e({...n,items:n.items.map((e=>({...e,content:t(e.content)})))});case _t.table:return e({...n,rows:n.rows.map((e=>e.map(t)))});case _t.termList:return e({...n,items:n.items.map((e=>({...e,term:{inlineContent:t(e.term.inlineContent)},definition:{content:t(e.definition.content)}})))});default:return e(n)}}))}return t(this.content)},forEach(e){function t(n=[]){n.forEach((n=>{switch(e(n),n.type){case _t.aside:t(n.content);break;case _t.paragraph:case St.emphasis:case St.strong:case St.inlineHead:case St.newTerm:case St.superscript:case St.subscript:case St.strikethrough:t(n.inlineContent);break;case _t.orderedList:case _t.unorderedList:n.items.forEach((e=>t(e.content)));break;case _t.dictionaryExample:t(n.summary);break;case _t.table:n.rows.forEach((e=>{e.forEach(t)}));break;case _t.termList:n.items.forEach((e=>{t(e.term.inlineContent),t(e.definition.content)}));break}}))}return t(this.content)},reduce(e,t){let n=t;return this.forEach((t=>{n=e(n,t)})),n}},computed:{plaintext(){const{references:e={}}=this;return this.reduce(((t,n)=>{if(n.type===_t.paragraph)return`${t}\n`;if(n.type===St.codeVoice)return`${t}${n.code}`;if(n.type===St.text)return`${t}${n.text}`;if(n.type===St.reference){const r=e[n.identifier]?.title??"";return`${t}${r}`}return t}),"").trim()}},BlockType:_t,InlineType:St},Ot=Dt,Nt=(0,g.Z)(Ot,Mt,Zt,!1,null,null,null),Rt=Nt.exports},7587:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=function(){var e=this,t=e._self._c;return t("aside",{class:e.kind,attrs:{"aria-label":e.kind}},[t("p",{staticClass:"label"},[e._v(e._s(e.name||e.$t(e.label)))]),e._t("default")],2)},i=[];const s={deprecated:"deprecated",experiment:"experiment",important:"important",note:"note",tip:"tip",warning:"warning"};var a={name:"Aside",props:{kind:{type:String,required:!0,validator:e=>Object.prototype.hasOwnProperty.call(s,e)},name:{type:String,required:!1}},computed:{label:({kind:e})=>`aside-kind.${e}`}},o=a,l=n(1001),c=(0,l.Z)(o,r,i,!1,null,"3ccce809",null),u=c.exports},5996:function(e,t,n){"use strict";n.d(t,{Z:function(){return J}});var r=function(){var e=this,t=e._self._c;return t("div",{staticClass:"code-listing",class:{"single-line":1===e.syntaxHighlightedLines.length},attrs:{"data-syntax":e.syntaxNameNormalized}},[e.fileName?t("Filename",{attrs:{isActionable:e.isFileNameActionable,fileType:e.fileType},on:{click:function(t){return e.$emit("file-name-click")}}},[e._v(e._s(e.fileName)+" ")]):e._e(),t("div",{staticClass:"container-general"},[t("pre",[t("CodeBlock",[e._l(e.syntaxHighlightedLines,(function(n,r){return[t("span",{key:r,class:["code-line-container",{highlighted:e.isHighlighted(r)}]},[e.showLineNumbers?t("span",{staticClass:"code-number",attrs:{"data-line-number":e.lineNumberFor(r)}}):e._e(),t("span",{staticClass:"code-line",domProps:{innerHTML:e._s(n)}})]),e._v("\n")]}))],2)],1)])],1)},i=[],s=n(3208),a=n(3078),o=n(3917),l=n(3390),c=l;const u={objectivec:["objective-c"]},d={bash:["sh","zsh"],c:["h"],cpp:["cc","c++","h++","hpp","hh","hxx","cxx"],css:[],diff:["patch"],http:["https"],java:["jsp"],javascript:["js","jsx","mjs","cjs"],json:[],llvm:[],markdown:["md","mkdown","mkd"],objectivec:["mm","objc","obj-c"].concat(u.objectivec),perl:["pl","pm"],php:[],python:["py","gyp","ipython"],ruby:["rb","gemspec","podspec","thor","irb"],scss:[],shell:["console","shellsession"],swift:[],xml:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],...{NODE_ENV:"production",VUE_APP_TITLE:"Documentation",BASE_URL:"{{BASE_PATH}}/"}.VUE_APP_HLJS_LANGUAGES?Object.fromEntries({NODE_ENV:"production",VUE_APP_TITLE:"Documentation",BASE_URL:"{{BASE_PATH}}/"}.VUE_APP_HLJS_LANGUAGES.split(",").map((e=>[e,[]]))):void 0},A=new Set(["markdown","swift"]),p=Object.entries(d),h=new Set(Object.keys(d)),g=new Map;async function f(e){const t=[e];try{return await t.reduce((async(e,t)=>{let r;await e,r=A.has(t)?await n(3685)(`./${t}`):await n(2122)(`./${t}.js`),c.registerLanguage(t,r.default)}),Promise.resolve()),!0}catch(r){return console.error(`Could not load ${e} file`),!1}}function m(e){if(h.has(e))return e;const t=p.find((([,t])=>t.includes(e)));return t?t[0]:null}function v(e){if(g.has(e))return g.get(e);const t=m(e);return g.set(e,t),t}c.configure({classPrefix:"syntax-",languages:[...h]});const b=async e=>{const t=v(e);return!(!t||c.listLanguages().includes(t))&&f(t)},y=/\r\n|\r|\n/g,C=/syntax-/;function I(e){return 0===e.length?[]:e.split(y)}function w(e){return(e.trim().match(y)||[]).length}function E(e){const t=document.createElement("template");return t.innerHTML=e,t.content.childNodes}function B(e){const{className:t}=e;if(!C.test(t))return null;const n=I(e.innerHTML).reduce(((e,n)=>`${e}${n}\n`),"");return E(n.trim())}function x(e){return Array.from(e.childNodes).forEach((e=>{if(w(e.textContent))try{const t=e.childNodes.length?x(e):B(e);t&&e.replaceWith(...t)}catch(t){console.error(t)}})),B(e)}function k(e,t){const n=m(t);if(!c.getLanguage(n))throw new Error(`Unsupported language for syntax highlighting: ${t}`);return c.highlight(e,{language:n,ignoreIllegals:!0}).value}function _(e,t){const n=e.join("\n"),r=k(n,t),i=document.createElement("code");return i.innerHTML=r,x(i),I(i.innerHTML)}var S=function(){var e=this,t=e._self._c;return t("span",{staticClass:"filename"},[e.isActionable?t("a",{attrs:{href:"#"},on:{click:function(t){return t.preventDefault(),e.$emit("click")}}},[t("FileIcon",{attrs:{fileType:e.fileType}}),e._t("default")],2):t("span",[t("FileIcon",{attrs:{fileType:e.fileType}}),e._t("default")],2)])},T=[],L=function(){var e=this,t=e._self._c;return"swift"===e.fileType?t("SwiftFileIcon",{staticClass:"file-icon"}):t("GenericFileIcon",{staticClass:"file-icon"})},P=[],Q=n(7834),M=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"generic-file-icon",attrs:{viewBox:"0 0 14 14",themeId:"generic-file"}},[t("path",{attrs:{d:"M8.033 1l3.967 4.015v7.985h-10v-12zM7.615 2h-4.615v10h8v-6.574z"}}),t("path",{attrs:{d:"M7 1h1v4h-1z"}}),t("path",{attrs:{d:"M7 5h5v1h-5z"}})])},Z=[],D=n(9742),O={name:"GenericFileIcon",components:{SVGIcon:D.Z}},N=O,R=n(1001),j=(0,R.Z)(N,M,Z,!1,null,null,null),G=j.exports,V={name:"CodeListingFileIcon",components:{SwiftFileIcon:Q.Z,GenericFileIcon:G},props:{fileType:String}},$=V,z=(0,R.Z)($,L,P,!1,null,"7c381064",null),q=z.exports,H={name:"CodeListingFilename",components:{FileIcon:q},props:{isActionable:{type:Boolean,default:()=>!1},fileType:String}},W=H,F=(0,R.Z)(W,S,T,!1,null,"c8c40662",null),U=F.exports,Y={name:"CodeListing",components:{Filename:U,CodeBlock:o.Z},data(){return{syntaxHighlightedLines:[]}},props:{fileName:String,isFileNameActionable:{type:Boolean,default:()=>!1},syntax:String,fileType:String,content:{type:Array,required:!0},startLineNumber:{type:Number,default:()=>1},highlights:{type:Array,default:()=>[]},showLineNumbers:{type:Boolean,default:()=>!1}},computed:{escapedContent:({content:e})=>e.map(s.Xv),highlightedLineNumbers(){return new Set(this.highlights.map((({line:e})=>e)))},syntaxNameNormalized(){const e={occ:a.Z.objectiveC.key.url};return e[this.syntax]||this.syntax}},watch:{content:{handler:"syntaxHighlightLines",immediate:!0}},methods:{isHighlighted(e){return this.highlightedLineNumbers.has(this.lineNumberFor(e))},lineNumberFor(e){return this.startLineNumber+e},async syntaxHighlightLines(){let e;try{await b(this.syntaxNameNormalized),e=_(this.content,this.syntaxNameNormalized)}catch(t){e=this.escapedContent}this.syntaxHighlightedLines=e.map((e=>""===e?"\n":e))}}},X=Y,K=(0,R.Z)(X,r,i,!1,null,"13e6923e",null),J=K.exports},2020:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=function(){var e=this,t=e._self._c;return t("WordBreak",{attrs:{tag:"code"}},[e._t("default")],2)},i=[],s=n(352),a={name:"CodeVoice",components:{WordBreak:s.Z}},o=a,l=n(1001),c=(0,l.Z)(o,r,i,!1,null,"05f4a5b7",null),u=c.exports},3938:function(e,t,n){"use strict";n.d(t,{Z:function(){return c}});var r=function(){var e=this,t=e._self._c;return t("div",{staticClass:"column",style:e.style},[e._t("default")],2)},i=[],s={name:"Column",props:{span:{type:Number,default:null}},computed:{style:({span:e})=>({"--col-span":e})}},a=s,o=n(1001),l=(0,o.Z)(a,r,i,!1,null,"0f654188",null),c=l.exports},889:function(e,t,n){"use strict";n.d(t,{Z:function(){return A}});var r=function(){var e=this,t=e._self._c;return t("div",{staticClass:"device-frame",class:e.classes,style:e.styles,attrs:{"data-device":e.device}},[t("div",{staticClass:"device-screen",class:{"with-device":e.currentDeviceAttrs}},[e._t("default")],2),t("div",{staticClass:"device"})])},i=[],s={},a=n(9089);const o=e=>e&&e!==1/0;var l={name:"DeviceFrame",props:{device:{type:String,required:!0}},provide:{insideDeviceFrame:!0},computed:{currentDeviceAttrs:({device:e})=>(0,a.$8)(["theme","device-frames",e],s[e]),styles:({toPixel:e,toUrl:t,toPct:n,currentDeviceAttrs:r={},toVal:i})=>{const{screenTop:s,screenLeft:a,screenWidth:o,frameWidth:l,lightUrl:c,darkUrl:u,screenHeight:d,frameHeight:A}=r;return{"--screen-top":n(s/A),"--screen-left":n(a/l),"--screen-width":n(o/l),"--screen-height":n(d/A),"--screen-aspect":i(o/d),"--frame-width":e(l),"--frame-aspect":i(l/A),"--device-light-url":t(c),"--device-dark-url":t(u)}},classes:({currentDeviceAttrs:e})=>({"no-device":!e})},methods:{toPixel:e=>o(e)?`${e}px`:null,toUrl:e=>o(e)?`url(${e})`:null,toPct:e=>o(e)?100*e+"%":null,toVal:e=>o(e)?e:null}},c=l,u=n(1001),d=(0,u.Z)(c,r,i,!1,null,"c2eac128",null),A=d.exports},8039:function(e,t,n){"use strict";n.d(t,{Z:function(){return m}});var r=function(){var e=this,t=e._self._c;return t(`h${e.level}`,{tag:"component",attrs:{id:e.anchor}},[e.shouldLink?t("router-link",{staticClass:"header-anchor",attrs:{to:{hash:`#${e.anchor}`},"data-after-text":e.$t("accessibility.in-page-link")},on:{click:function(t){return e.handleFocusAndScroll(e.anchor)}}},[e._t("default"),t("LinkIcon",{staticClass:"icon",attrs:{"aria-hidden":"true"}})],2):[e._t("default")]],2)},i=[],s=n(3704),a=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"link-icon",attrs:{viewBox:"0 0 20 20"}},[t("path",{attrs:{d:"M19.34,4.88L15.12,.66c-.87-.87-2.3-.87-3.17,0l-3.55,3.56-1.38,1.38-1.4,1.4c-.47,.47-.68,1.09-.64,1.7,.02,.29,.09,.58,.21,.84,.11,.23,.24,.44,.43,.63l4.22,4.22h0l.53-.53,.53-.53h0l-4.22-4.22c-.29-.29-.29-.77,0-1.06l1.4-1.4,.91-.91,.58-.58,.55-.55,2.9-2.9c.29-.29,.77-.29,1.06,0l4.22,4.22c.29,.29,.29,.77,0,1.06l-2.9,2.9c.14,.24,.24,.49,.31,.75,.08,.32,.11,.64,.09,.96l3.55-3.55c.87-.87,.87-2.3,0-3.17Z"}}),t("path",{attrs:{d:"M14.41,9.82s0,0,0,0l-4.22-4.22h0l-.53,.53-.53,.53h0l4.22,4.22c.29,.29,.29,.77,0,1.06l-1.4,1.4-.91,.91-.58,.58-.55,.55h0l-2.9,2.9c-.29,.29-.77,.29-1.06,0L1.73,14.04c-.29-.29-.29-.77,0-1.06l2.9-2.9c-.14-.24-.24-.49-.31-.75-.08-.32-.11-.64-.09-.97L.68,11.93c-.87,.87-.87,2.3,0,3.17l4.22,4.22c.87,.87,2.3,.87,3.17,0l3.55-3.55,1.38-1.38,1.4-1.4c.47-.47,.68-1.09,.64-1.7-.02-.29-.09-.58-.21-.84-.11-.22-.24-.44-.43-.62Z"}})])},o=[],l=n(9742),c={name:"LinkIcon",components:{SVGIcon:l.Z}},u=c,d=n(1001),A=(0,d.Z)(u,a,o,!1,null,null,null),p=A.exports,h={name:"LinkableHeading",mixins:[s.Z],components:{LinkIcon:p},props:{anchor:{type:String,required:!1},level:{type:Number,default:()=>2,validator:e=>e>=1&&e<=6}},inject:{enableMinimized:{default:()=>!1},isTargetIDE:{default:()=>!1}},computed:{shouldLink:({anchor:e,enableMinimized:t,isTargetIDE:n})=>!!e&&!t&&!n}},g=h,f=(0,d.Z)(g,r,i,!1,null,"24fddf6a",null),m=f.exports},9882:function(e,t,n){"use strict";n.d(t,{Z:function(){return O}});var r=function(){var e=this,t=e._self._c;return t(e.refComponent,{tag:"component",attrs:{url:e.urlWithParams,"is-active":e.isActiveComputed}},[e._t("default")],2)},i=[],s=n(2449),a=n(7192),o=n(4589),l=function(){var e=this,t=e._self._c;return t("ReferenceExternal",e._b({},"ReferenceExternal",e.$props,!1),[t("CodeVoice",[e._t("default")],2)],1)},c=[],u=function(){var e=this,t=e._self._c;return e.isActive?t("a",{attrs:{href:e.url}},[e._t("default")],2):t("span",[e._t("default")],2)},d=[],A={name:"ReferenceExternal",props:{url:{type:String,required:!0},isActive:{type:Boolean,default:!0}}},p=A,h=n(1001),g=(0,h.Z)(p,u,d,!1,null,null,null),f=g.exports,m=n(2020),v={name:"ReferenceExternalSymbol",props:f.props,components:{ReferenceExternal:f,CodeVoice:m.Z}},b=v,y=(0,h.Z)(b,l,c,!1,null,null,null),C=y.exports,I=function(){var e=this,t=e._self._c;return t("ReferenceInternal",e._b({},"ReferenceInternal",e.$props,!1),[t("CodeVoice",[e._t("default")],2)],1)},w=[],E=function(){var e=this,t=e._self._c;return e.isActive?t("router-link",{attrs:{to:e.url}},[e._t("default")],2):t("span",[e._t("default")],2)},B=[],x={name:"ReferenceInternal",props:{url:{type:String,required:!0},isActive:{type:Boolean,default:!0}}},k=x,_=(0,h.Z)(k,E,B,!1,null,null,null),S=_.exports,T={name:"ReferenceInternalSymbol",props:S.props,components:{ReferenceInternal:S,CodeVoice:m.Z}},L=T,P=(0,h.Z)(L,I,w,!1,null,null,null),Q=P.exports,M={name:"Reference",computed:{isInternal({url:e}){if(!e.startsWith("/")&&!e.startsWith("#"))return!1;const{resolved:{name:t}={}}=this.$router.resolve(e)||{};return t!==o.vL},isSymbolReference(){return"symbol"===this.kind&&!this.hasInlineFormatting&&(this.role===a.L.symbol||this.role===a.L.dictionarySymbol)},isDisplaySymbol({isSymbolReference:e,titleStyle:t,ideTitle:n}){return n?e&&"symbol"===t:e},refComponent({isInternal:e,isDisplaySymbol:t}){return e?t?Q:S:t?C:f},urlWithParams({isInternal:e}){return e?(0,s.Q2)(this.url,this.$route.query):this.url},isActiveComputed({url:e,isActive:t}){return!(!e||!t)}},props:{url:{type:String,required:!0},kind:{type:String,required:!1},role:{type:String,required:!1},isActive:{type:Boolean,required:!1,default:!0},ideTitle:{type:String,required:!1},titleStyle:{type:String,required:!1},hasInlineFormatting:{type:Boolean,default:!1}}},Z=M,D=(0,h.Z)(Z,r,i,!1,null,null,null),O=D.exports},3002:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=function(){var e=this,t=e._self._c;return t("div",{staticClass:"row",class:{"with-columns":e.columns},style:e.style},[e._t("default")],2)},i=[],s=n(5381),a={name:"Row",props:{columns:{type:Object,required:!1,validator:e=>Object.entries(e).every((([e,t])=>s.L3[e]&&"number"===typeof t))},gap:{type:Number,required:!1}},computed:{style:({columns:e={},gap:t})=>({"--col-count-large":e.large,"--col-count-medium":e.medium,"--col-count-small":e.small||1,"--col-gap":t&&`${t}px`})}},o=a,l=n(1001),c=(0,l.Z)(o,r,i,!1,null,"1bcb2d0f",null),u=c.exports},1295:function(e,t,n){"use strict";n.d(t,{Z:function(){return d}});var r=n(5953);const i={link:"link",reference:"reference",text:"text"};var s,a,o={name:"DestinationDataProvider",mixins:[r.Z],props:{destination:{type:Object,required:!0,default:()=>({})}},inject:{isTargetIDE:{default:()=>!1}},constants:{DestinationType:i},computed:{isExternal:({reference:e,destination:t})=>e.type===i.link||t.type===i.link,shouldAppendOpensInBrowser:({isExternal:e,isTargetIDE:t})=>e&&t,reference:({references:e,destination:t})=>e[t.identifier]||{},linkUrl:({destination:e,reference:t})=>({[i.link]:e.destination,[i.reference]:t.url,[i.text]:e.text}[e.type]),linkTitle:({reference:e,destination:t})=>({[i.link]:t.title,[i.reference]:t.overridingTitle||e.title,[i.text]:""}[t.type])},methods:{formatAriaLabel(e){return this.shouldAppendOpensInBrowser?`${e} (opens in browser)`:e}},render(){return this.$scopedSlots.default({url:this.linkUrl||"",title:this.linkTitle||"",formatAriaLabel:this.formatAriaLabel,isExternal:this.isExternal})}},l=o,c=n(1001),u=(0,c.Z)(l,s,a,!1,null,null,null),d=u.exports},1105:function(e,t,n){"use strict";n.d(t,{Z:function(){return Ae}});var r=function(){var e=this,t=e._self._c;return t("Pager",{class:["TopicsLinkCardGrid",e.topicStyle],attrs:{"aria-label":e.$t("links-grid.label"),pages:e.pages},scopedSlots:e._u([{key:"page",fn:function({page:n}){return[t("Row",{attrs:{columns:{large:e.compactCards?3:2,medium:e.compactCards?3:2}}},e._l(n,(function(n){return t("Column",{key:n.title},[t("TopicsLinkCardGridItem",{attrs:{item:n,compact:e.compactCards}})],1)})),1)]}}])},[t("BreakpointEmitter",{on:{change:e.handleBreakpointChange}})],1)},i=[],s=n(7188),a=n(3938),o=function(){var e=this,t=e._self._c;return t("div",{class:["pager",{"with-compact-controls":e.shouldUseCompactControls}],attrs:{role:"region","aria-roledescription":e.$t("pager.roledescription")}},[1===e.pages.length?[e._t("page",null,{page:e.pages[0]})]:[t("div",{staticClass:"container"},[t("Gutter",{staticClass:"left"},[t("ControlPrevious",{attrs:{disabled:!e.hasPreviousPage},nativeOn:{click:function(t){return e.previous.apply(null,arguments)}}})],1),t("div",{ref:"viewport",staticClass:"viewport",attrs:{role:"group"}},e._l(e.keyedPages,(function({page:n,key:r},i){return t("div",{key:r,ref:"pages",refInFor:!0,class:["page",e.pageStates(i)],attrs:{"aria-label":e.$t("pager.page.label",{index:i+1,count:e.keyedPages.length}),id:r}},[e._t("page",null,{page:n})],2)})),0),t("Gutter",{staticClass:"right"},[t("ControlNext",{attrs:{disabled:!e.hasNextPage},nativeOn:{click:function(t){return e.next.apply(null,arguments)}}})],1)],1),t("div",{staticClass:"compact-controls",attrs:{role:"group","aria-label":"Controls"}},[t("ControlPrevious",{attrs:{disabled:!e.hasPreviousPage},nativeOn:{click:function(t){return e.previous.apply(null,arguments)}}}),t("ControlNext",{attrs:{disabled:!e.hasNextPage},nativeOn:{click:function(t){return e.next.apply(null,arguments)}}})],1),t("div",{staticClass:"indicators"},e._l(e.keyedPages,(function({key:n},r){return t("a",{key:n,class:["indicator",e.pageStates(r)],attrs:{"aria-current":e.isActivePage(r),href:`#${n}`},on:{click:function(t){return e.setActivePage(t,r)}}})})),0)],e._t("default")],2)},l=[],c=function(){var e=this,t=e._self._c;return t("button",{class:["pager-control",e.action],attrs:{"aria-label":e.$t(`pager.control.navigate-${e.action}`)}},[t("ChevronRoundedIcon",{staticClass:"icon"})],1)},u=[],d=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"chevron-rounded-icon",attrs:{viewBox:"0 0 14 25",themeId:"chevron-rounded"}},[t("path",{attrs:{d:"M1,24.4a.9.9,0,0,0,.7-.3L13.4,13a1,1,0,0,0,0-1.6L1.7.3A.9.9,0,0,0,1,0,.9.9,0,0,0,0,1a.9.9,0,0,0,.3.7l11,10.5L.3,22.7a.9.9,0,0,0-.3.7A.9.9,0,0,0,1,24.4Z"}})])},A=[],p=n(9742),h={name:"ChevronRoundedIcon",components:{SVGIcon:p.Z}},g=h,f=n(1001),m=(0,f.Z)(g,d,A,!1,null,null,null),v=m.exports;const b={previous:"previous",next:"next"};var y={name:"PagerControl",components:{ChevronRoundedIcon:v},props:{action:{type:String,required:!0,validator:e=>Object.hasOwnProperty.call(b,e)}},Action:b},C=y,I=(0,f.Z)(C,c,u,!1,null,"58c8390a",null),w=I.exports,E=n(5381),B=n(220);const x=174;function k(e){return e.scrollIntoView({behavior:"auto",block:"nearest",inline:"start"}),new Promise((t=>{const n=60,r=2;let i=null,s=0;function a(){const o=e.getBoundingClientRect().left;s>r&&(o===i||s>=n)?t():(i=o,s+=1,requestAnimationFrame(a))}requestAnimationFrame(a)}))}var _={name:"Pager",components:{ControlNext:{render(e){return e(w,{props:{action:w.Action.next}})}},ControlPrevious:{render(e){return e(w,{props:{action:w.Action.previous}})}},Gutter:{render(e){return e("div",{class:"gutter"},this.$slots.default)}}},props:{pages:{type:Array,required:!0,validator:e=>e.length>0}},data:()=>({activePageIndex:0,appState:B.Z.state}),computed:{indices:({keyedPages:e})=>e.reduce(((e,t,n)=>({...e,[t.key]:n})),{}),keyedPages:({_uid:e,pages:t})=>t.map(((t,n)=>({key:`pager-${e}-page-${n}`,page:t}))),hasNextPage:({activePageIndex:e,pages:t})=>ee>0,contentWidth:({appState:e})=>e.contentWidth,shouldUseCompactControls:({contentWidth:e})=>window.innerWidth>E.kB["default"].large.minWidth?e=this.$refs.pages.length)return;const n=this.$refs.pages[t];this.pauseObservingPages(),await k(n),this.startObservingPages(),this.activePageIndex=t},next(e){this.setActivePage(e,this.activePageIndex+1)},previous(e){this.setActivePage(e,this.activePageIndex-1)},observePages(e){const t=e.find((e=>e.isIntersecting))?.target?.id;t&&(this.activePageIndex=this.indices[t])},setupObserver(){this.observer=new IntersectionObserver(this.observePages,{root:this.$refs.viewport,threshold:.5}),this.startObservingPages()},startObservingPages(){this.$refs.pages.forEach((e=>{this.observer?.observe(e)}))},pauseObservingPages(){this.$refs.pages.forEach((e=>{this.observer?.unobserve(e)}))}},mounted(){this.pages.length>1&&this.setupObserver()},beforeDestroy(){this.observer?.disconnect()}},S=_,T=(0,f.Z)(S,o,l,!1,null,"1ed6aae0",null),L=T.exports,P=n(3002),Q=n(3946),M=n(5654),Z=function(){var e=this,t=e._self._c;return t("Card",{staticClass:"reference-card-grid-item",attrs:{url:e.item.url,image:e.imageReferences.card,title:e.item.title,"floating-style":"",size:e.cardSize,"link-text":e.compact?"":e.$t(e.linkText)},scopedSlots:e._u([e.imageReferences.card?null:{key:"cover",fn:function({classes:n}){return[t("div",{staticClass:"reference-card-grid-item__image",class:n},[t("TopicTypeIcon",{staticClass:"reference-card-grid-item__icon",attrs:{type:e.item.role,"image-override":e.references[e.imageReferences.icon]}})],1)]}}],null,!0)},[e.compact?e._e():t("ContentNode",{attrs:{content:e.item.abstract}})],1)},D=[],O=function(){var e=this,t=e._self._c;return t("Reference",e._b({staticClass:"card",class:e.classes,attrs:{url:e.url}},"Reference",e.linkAriaTags,!1),[t("CardCover",{attrs:{variants:e.imageVariants,rounded:e.floatingStyle,alt:e.imageReference.alt,"aria-hidden":"true"},scopedSlots:e._u([{key:"default",fn:function(t){return[e._t("cover",null,null,t)]}}],null,!0)}),t("div",{staticClass:"details",attrs:{"aria-hidden":"true"}},[e.eyebrow?t("div",{staticClass:"eyebrow",attrs:{id:e.eyebrowId,"aria-label":e.formatAriaLabel(`- ${e.eyebrow}`)}},[e._v(" "+e._s(e.eyebrow)+" ")]):e._e(),t("div",{staticClass:"title",attrs:{id:e.titleId}},[e._v(" "+e._s(e.title)+" ")]),e.$slots.default?t("div",{staticClass:"card-content",attrs:{id:e.contentId}},[e._t("default")],2):e._e(),e.linkText?t(e.hasButton?"ButtonLink":"div",{tag:"component",staticClass:"link"},[e._v(" "+e._s(e.linkText)+" "),e.showExternalLinks?t("DiagonalArrowIcon",{staticClass:"icon-inline link-icon"}):e.hasButton?e._e():t("InlineChevronRightIcon",{staticClass:"icon-inline link-icon"})],1):e._e()],1)],1)},N=[],R=n(5281),j=n(8785),G=n(6817),V=n(9882),$={small:"small",large:"large"},z=n(5953),q=function(){var e=this,t=e._self._c;return t("div",{staticClass:"card-cover-wrap",class:{rounded:e.rounded}},[e._t("default",(function(){return[t("ImageAsset",{staticClass:"card-cover",attrs:{variants:e.variants,alt:e.alt}})]}),{classes:"card-cover"})],2)},H=[],W=n(6769),F={name:"CardCover",components:{ImageAsset:W.Z},props:{variants:{type:Array,required:!0},rounded:{type:Boolean,default:!1},alt:{type:String,default:null}}},U=F,Y=(0,f.Z)(U,q,H,!1,null,"28b14a83",null),X=Y.exports,K={name:"Card",components:{Reference:V.Z,DiagonalArrowIcon:G.Z,InlineChevronRightIcon:j.Z,CardCover:X,ButtonLink:R.Z},constants:{CardSize:$},mixins:[z.Z],computed:{titleId:({_uid:e})=>`card_title_${e}`,contentId:({_uid:e})=>`card_content_${e}`,eyebrowId:({_uid:e})=>`card_eyebrow_${e}`,linkAriaTags:({titleId:e,eyebrowId:t,contentId:n,eyebrow:r,$slots:i})=>({"aria-labelledby":e.concat(r?` ${t}`:""),"aria-describedby":i.default?`${n}`:null}),classes:({size:e,floatingStyle:t})=>[e,{"floating-style":t}],imageReference:({image:e,references:t})=>t[e]||{},imageVariants:({imageReference:e})=>e.variants||[]},props:{linkText:{type:String,required:!1},url:{type:String,required:!1,default:""},eyebrow:{type:String,required:!1},image:{type:String,required:!1},size:{type:String,validator:e=>Object.prototype.hasOwnProperty.call($,e)},title:{type:String,required:!0},hasButton:{type:Boolean,default:()=>!1},floatingStyle:{type:Boolean,default:!1},showExternalLinks:{type:Boolean,default:!1},formatAriaLabel:{type:Function,default:e=>e}}},J=K,ee=(0,f.Z)(J,O,N,!1,null,"0f7a4f31",null),te=ee.exports,ne=n(5921),re=n(7192);const ie={[re.L.article]:"documentation.card.read-article",[re.L.overview]:"documentation.card.start-tutorial",[re.L.collection]:"documentation.card.view-api",[re.L.symbol]:"documentation.card.view-symbol",[re.L.sampleCode]:"documentation.card.view-sample-code"};var se={name:"TopicsLinkCardGridItem",components:{TopicTypeIcon:ne.Z,Card:te,ContentNode:()=>Promise.resolve().then(n.bind(n,9519))},mixins:[z.Z],props:{item:{type:Object,required:!0},compact:{type:Boolean,default:!0}},computed:{imageReferences:({item:e})=>(e.images||[]).reduce(((e,t)=>(e[t.type]=t.identifier,e)),{icon:null,card:null}),linkText:({item:e})=>ie[e.role]||"documentation.card.learn-more",cardSize:({compact:e})=>e?void 0:$.large}},ae=se,oe=(0,f.Z)(ae,Z,D,!1,null,"87dd3302",null),le=oe.exports,ce={name:"TopicsLinkCardGrid",components:{BreakpointEmitter:s["default"],Column:a.Z,Pager:L,Row:P.Z,TopicsLinkCardGridItem:le},data:()=>({breakpoint:E.L3.large}),props:{items:{type:Array,required:!0},pageSize:{type:Number,required:!1},topicStyle:{type:String,default:Q.o.compactGrid,validator:e=>e===Q.o.compactGrid||e===Q.o.detailedGrid},usePager:{type:Boolean,default:!1}},computed:{compactCards:({topicStyle:e})=>e===Q.o.compactGrid,defaultPageSize:({breakpoint:e,items:t,topicStyle:n,usePager:r})=>r?{[Q.o.compactGrid]:{[E.L3.large]:6,[E.L3.medium]:6,[E.L3.small]:1},[Q.o.detailedGrid]:{[E.L3.large]:4,[E.L3.medium]:2,[E.L3.small]:1}}[n][e]:t.length,pages:({items:e,defaultPageSize:t,pageSize:n})=>(0,M.Md)(e,n||t)},methods:{handleBreakpointChange(e){this.breakpoint=e}}},ue=ce,de=(0,f.Z)(ue,r,i,!1,null,null,null),Ae=de.exports},1576:function(e,t,n){"use strict";n.d(t,{Z:function(){return g}});var r=function(){var e=this,t=e._self._c;return t("div",{staticClass:"col",class:e.classes},[e._t("default")],2)},i=[];const s=0,a=12,o=new Set(["large","medium","small"]),l=e=>({type:Object,default:()=>({}),validator:t=>Object.keys(t).every((n=>o.has(n)&&e(t[n])))}),c=l((e=>"boolean"===typeof e)),u=l((e=>"number"===typeof e&&e>=s&&e<=a));var d={name:"GridColumn",props:{isCentered:c,isUnCentered:c,span:{...u,default:()=>({large:a})}},computed:{classes:function(){return{[`large-${this.span.large}`]:void 0!==this.span.large,[`medium-${this.span.medium}`]:void 0!==this.span.medium,[`small-${this.span.small}`]:void 0!==this.span.small,"large-centered":!!this.isCentered.large,"medium-centered":!!this.isCentered.medium,"small-centered":!!this.isCentered.small,"large-uncentered":!!this.isUnCentered.large,"medium-uncentered":!!this.isUnCentered.medium,"small-uncentered":!!this.isUnCentered.small}}}},A=d,p=n(1001),h=(0,p.Z)(A,r,i,!1,null,"2ee3ad8b",null),g=h.exports},9649:function(e,t,n){"use strict";n.d(t,{Z:function(){return c}});var r=function(){var e=this,t=e._self._c;return t("div",{staticClass:"row"},[e._t("default")],2)},i=[],s={name:"GridRow"},a=s,o=n(1001),l=(0,o.Z)(a,r,i,!1,null,"be73599c",null),c=l.exports},5692:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"article-icon",attrs:{viewBox:"0 0 14 14",themeId:"article"}},[t("path",{attrs:{d:"M8.033 1l3.967 4.015v7.985h-10v-12zM7.615 2h-4.615v10h8v-6.574z"}}),t("path",{attrs:{d:"M7 1h1v4h-1z"}}),t("path",{attrs:{d:"M7 5h5v1h-5z"}})])},i=[],s=n(9742),a={name:"ArticleIcon",components:{SVGIcon:s.Z}},o=a,l=n(1001),c=(0,l.Z)(o,r,i,!1,null,null,null),u=c.exports},7775:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"curly-brackets-icon",attrs:{viewBox:"0 0 14 14",themeId:"curly-brackets"}},[t("path",{attrs:{d:"M9.987 14h-0.814v-0.916h0.36c0.137 0 0.253-0.038 0.349-0.116 0.099-0.080 0.179-0.188 0.239-0.318 0.064-0.134 0.11-0.298 0.139-0.483 0.031-0.186 0.045-0.38 0.045-0.58v-2.115c0-0.417 0.046-0.781 0.139-1.083 0.092-0.3 0.2-0.554 0.322-0.754 0.127-0.203 0.246-0.353 0.366-0.458 0.087-0.076 0.155-0.131 0.207-0.169-0.052-0.037-0.12-0.093-0.207-0.167-0.12-0.105-0.239-0.255-0.366-0.459-0.122-0.2-0.23-0.453-0.322-0.754-0.093-0.3-0.139-0.665-0.139-1.082v-2.13c0-0.199-0.014-0.392-0.045-0.572-0.029-0.182-0.076-0.345-0.139-0.483-0.060-0.137-0.141-0.246-0.239-0.328-0.095-0.076-0.212-0.115-0.349-0.115h-0.36v-0.916h0.814c0.442 0 0.788 0.18 1.030 0.538 0.238 0.352 0.358 0.826 0.358 1.407v2.236c0 0.3 0.015 0.597 0.044 0.886 0.030 0.287 0.086 0.544 0.164 0.765 0.077 0.216 0.184 0.392 0.318 0.522 0.129 0.124 0.298 0.188 0.503 0.188h0.058v0.916h-0.058c-0.206 0-0.374 0.064-0.503 0.188-0.134 0.129-0.242 0.305-0.318 0.521-0.078 0.223-0.134 0.48-0.164 0.766-0.029 0.288-0.044 0.587-0.044 0.884v2.236c0 0.582-0.12 1.055-0.358 1.409-0.242 0.358-0.588 0.538-1.030 0.538z"}}),t("path",{attrs:{d:"M4.827 14h-0.814c-0.442 0-0.788-0.18-1.030-0.538-0.238-0.352-0.358-0.825-0.358-1.409v-2.221c0-0.301-0.015-0.599-0.045-0.886-0.029-0.287-0.085-0.544-0.163-0.764-0.077-0.216-0.184-0.393-0.318-0.522-0.131-0.127-0.296-0.188-0.503-0.188h-0.058v-0.916h0.058c0.208 0 0.373-0.063 0.503-0.188 0.135-0.129 0.242-0.304 0.318-0.522 0.078-0.22 0.134-0.477 0.163-0.765 0.030-0.286 0.045-0.585 0.045-0.886v-2.251c0-0.582 0.12-1.055 0.358-1.407 0.242-0.358 0.588-0.538 1.030-0.538h0.814v0.916h-0.36c-0.138 0-0.252 0.038-0.349 0.116-0.099 0.079-0.179 0.189-0.239 0.327-0.064 0.139-0.11 0.302-0.141 0.483-0.029 0.18-0.044 0.373-0.044 0.572v2.13c0 0.417-0.046 0.782-0.138 1.082-0.092 0.302-0.201 0.556-0.324 0.754-0.123 0.201-0.246 0.356-0.366 0.459-0.086 0.074-0.153 0.13-0.206 0.167 0.052 0.038 0.12 0.093 0.206 0.169 0.12 0.103 0.243 0.258 0.366 0.458s0.232 0.453 0.324 0.754c0.092 0.302 0.138 0.666 0.138 1.083v2.115c0 0.2 0.015 0.394 0.044 0.58 0.030 0.186 0.077 0.349 0.139 0.482 0.062 0.132 0.142 0.239 0.241 0.32 0.096 0.079 0.21 0.116 0.349 0.116h0.36z"}})])},i=[],s=n(9742),a={name:"CurlyBracketsIcon",components:{SVGIcon:s.Z}},o=a,l=n(1001),c=(0,l.Z)(o,r,i,!1,null,null,null),u=c.exports},6817:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"diagonal-arrow",attrs:{viewBox:"0 0 14 14",themeId:"diagonal-arrow"}},[t("path",{attrs:{d:"M0.010 12.881l10.429-10.477-3.764 0.824-0.339-1.549 7.653-1.679-1.717 7.622-1.546-0.349 0.847-3.759-10.442 10.487z"}})])},i=[],s=n(9742),a={name:"DiagonalArrowIcon",components:{SVGIcon:s.Z}},o=a,l=n(1001),c=(0,l.Z)(o,r,i,!1,null,null,null),u=c.exports},8633:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=function(){var e=this,t=e._self._c;return t("SVGIcon",{attrs:{viewBox:"0 0 14 14",themeId:"path"}},[t("path",{attrs:{d:"M0 0.948h2.8v2.8h-2.8z"}}),t("path",{attrs:{d:"M11.2 10.252h2.8v2.8h-2.8z"}}),t("path",{attrs:{d:"M6.533 1.852h0.933v10.267h-0.933z"}}),t("path",{attrs:{d:"M2.8 1.852h4.667v0.933h-4.667z"}}),t("path",{attrs:{d:"M6.533 11.186h4.667v0.933h-4.667z"}})])},i=[],s=n(9742),a={name:"PathIcon",components:{SVGIcon:s.Z}},o=a,l=n(1001),c=(0,l.Z)(o,r,i,!1,null,null,null),u=c.exports},6698:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"play-icon",attrs:{viewBox:"0 0 14 14",themeId:"play"}},[t("path",{attrs:{d:"M7 0.5c3.59 0 6.5 2.91 6.5 6.5s-2.91 6.5-6.5 6.5c-3.59 0-6.5-2.91-6.5-6.5v0c0-3.59 2.91-6.5 6.5-6.5v0zM7 1.5c-3.038 0-5.5 2.462-5.5 5.5s2.462 5.5 5.5 5.5c3.038 0 5.5-2.462 5.5-5.5v0c0-3.038-2.462-5.5-5.5-5.5v0z"}}),t("path",{attrs:{d:"M10.195 7.010l-5 3v-6l5 3z"}})])},i=[],s=n(9742),a={name:"PlayIcon",components:{SVGIcon:s.Z}},o=a,l=n(1001),c=(0,l.Z)(o,r,i,!1,null,null,null),u=c.exports},7834:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"swift-file-icon",attrs:{viewBox:"0 0 15 14",themeId:"swift-file"}},[t("path",{attrs:{d:"M14.93,13.56A2.15,2.15,0,0,0,15,13a5.37,5.37,0,0,0-1.27-3.24A6.08,6.08,0,0,0,14,7.91,9.32,9.32,0,0,0,9.21.31a8.51,8.51,0,0,1,1.78,5,6.4,6.4,0,0,1-.41,2.18A45.06,45.06,0,0,1,3.25,1.54,44.57,44.57,0,0,0,7.54,6.9,45.32,45.32,0,0,1,1.47,2.32,35.69,35.69,0,0,0,8.56,9.94a6.06,6.06,0,0,1-3.26.85A9.48,9.48,0,0,1,0,8.91a10,10,0,0,0,8.1,4.72c2.55,0,3.25-1.2,4.72-1.2a2.09,2.09,0,0,1,1.91,1.15C14.79,13.69,14.88,13.75,14.93,13.56Z"}})])},i=[],s=n(9742),a={name:"SwiftFileIcon",components:{SVGIcon:s.Z}},o=a,l=n(1001),c=(0,l.Z)(o,r,i,!1,null,"c01a6890",null),u=c.exports},9001:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"technology-icon",attrs:{viewBox:"0 0 14 14",themeId:"technology"}},[t("path",{attrs:{d:"M3.39,9l3.16,1.84.47.28.47-.28L10.61,9l.45.26,1.08.63L7,12.91l-5.16-3,1.08-.64L3.39,9M7,0,0,4.1,2.47,5.55,0,7,2.47,8.44,0,9.9,7,14l7-4.1L11.53,8.45,14,7,11.53,5.56,14,4.1ZM7,7.12,5.87,6.45l-1.54-.9L3.39,5,1.85,4.1,7,1.08l5.17,3L10.6,5l-.93.55-1.54.91ZM7,10,3.39,7.9,1.85,7,3.4,6.09,4.94,7,7,8.2,9.06,7,10.6,6.1,12.15,7l-1.55.9Z"}})])},i=[],s=n(9742),a={name:"TechnologyIcon",components:{SVGIcon:s.Z}},o=a,l=n(1001),c=(0,l.Z)(o,r,i,!1,null,null,null),u=c.exports},8638:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"tutorial-icon",attrs:{viewBox:"0 0 14 14",themeId:"tutorial"}},[t("path",{attrs:{d:"M0.933 6.067h3.733v1.867h-3.733v-1.867z"}}),t("path",{attrs:{d:"M0.933 1.867h3.733v1.867h-3.733v-1.867z"}}),t("path",{attrs:{d:"M13.067 1.867v10.267h-7.467v-10.267zM12.133 2.8h-5.6v8.4h5.6z"}}),t("path",{attrs:{d:"M0.933 10.267h3.733v1.867h-3.733v-1.867z"}})])},i=[],s=n(9742),a={name:"TutorialIcon",components:{SVGIcon:s.Z}},o=a,l=n(1001),c=(0,l.Z)(o,r,i,!1,null,null,null),u=c.exports},6769:function(e,t,n){"use strict";n.d(t,{Z:function(){return f}});var r=function(){var e=this,t=e._self._c;return e.fallbackImageSrcSet?t("img",{staticClass:"fallback",attrs:{title:e.$t("error.image"),decoding:"async",alt:e.alt,srcset:e.fallbackImageSrcSet}}):t("picture",[e.prefersAuto&&e.darkVariantAttributes?t("source",{attrs:{media:"(prefers-color-scheme: dark)",srcset:e.darkVariantAttributes.srcSet}}):e._e(),e.prefersDark&&e.darkVariantAttributes?t("img",e._b({ref:"img",attrs:{decoding:"async","data-orientation":e.orientation,loading:e.loading,alt:e.alt,width:e.darkVariantAttributes.width||e.optimalWidth,height:e.darkVariantAttributes.width||e.optimalWidth?"auto":null},on:{error:e.handleImageLoadError}},"img",e.darkVariantAttributes,!1)):t("img",e._b({ref:"img",attrs:{decoding:"async","data-orientation":e.orientation,loading:e.loading,alt:e.alt,width:e.defaultAttributes.width||e.optimalWidth,height:e.defaultAttributes.width||e.optimalWidth?"auto":null},on:{error:e.handleImageLoadError}},"img",e.defaultAttributes,!1))])},i=[],s=n(5947),a={props:{variants:{type:Array,required:!0}},computed:{variantsGroupedByAppearance(){return(0,s.XV)(this.variants)},lightVariants(){return(0,s.u)(this.variantsGroupedByAppearance.light)},darkVariants(){return(0,s.u)(this.variantsGroupedByAppearance.dark)}}},o=n(4030),l=n(9804),c="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAyAAAAJZCAYAAABRKlHVAAAACXBIWXMAABYlAAAWJQFJUiTwAAAXvUlEQVR4nO3d72pU57vH4aWmSoIBqSGpQaFgsVDoMexj22e2D6Cvav8plVq0amK0rbGJ9V83d+hs/Llb80wy6ztr1lwXBPoizcyamRfz8XmedZ/56quv/qcDAAAIWOm67r+80AAAQMJZrzIAAJAiQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQs+KlBmBR3L59u1tdXe0uX77cra2ted8AFpAAAWAhHBwcdPv7+0c/u7u73fnz57utra3u0qVLR/8NwGIQIAAshCdPnvzH03z58mV37969o5+KkPqplREAhk2AALAQ3g+Qd/32229HPxUjm5ub3fb2tjcVYKAcQgdg8Cou3rx5c+zTrN9ZWfFvawBDJkAAGLwKkFa2YQEMmwABYNBqVeND26/eVedAzp075w0FGDABAsCgTbP6UQECwLAJEAAGbWdnp+np1cqH7VcAwydAABisutXu4eFh09Oz+gGwGAQIAIO1t7fX/NTq9rsADJ8AAWCwWg+f1yT0tbU1byTAAhAgAAzS/v7+0RasFs5+ACwOAQLAILWufpSNjQ1vIsCCECAADFLr7XfX19ePtmABsBgECACDU6sfNYCwhe1XAItFgAAwOK2rHzX7w+13ARbLivcL4MNu377tFZqRGzduHPuH6uB5a4CcPXu2u3PnziCubaLlGgGWmQABOEbdjYnTa90q9fTp0+bHevXq1dEPAIvDFiwAIlq3Su3s7HhDAEZMgADQu9azGgcHB93r16+9IQAjJkAA6F3r6sfe3p43A2DkBAgAvWs9/zHN8EEAFpMAAaBXNSSwhgUep+589fbtW28GwMgJEAB61br96vHjx94IgCUgQADoVcv2q5p6/uzZM28EwBIQIAD0prZfra2tHfvnWwcPArD4DCIEmIH6ot160HqZtJz96KaY/XHmzJnuk08+mcsrWAfka0o7AKcjQABm4MKFC9329raX8gTqS/3h4WHT//jxxx/P7XV+/vy5AAGYAVuwAJiraSafb25uerMAFpwAAWCuWs9/tJ4nAWDYBAgAc7O/v9+8rWlra8sbBTACAgSAuZlm8nnrPBEAhk2AADAXNfujdftV3U2rtmABsPgECABzUfFREdLCLY4BxkOAADAXrasf586ds/0KYEQECABxdfC8NUAqPipCABgHAQJAXGt8dLZfAYyOAAEgrnX4YB08rwPoAIyHAAEg6uDgoHn2h7MfAOOz4j0FWCz1Bf758+fd69ev/+N5r6ysdBcvXhz8tPDd3d3m3zV8EGB8BAjAAqgVg9q2VIP7jrt1bW1bqnMT9eV9iIe3W89/rK6umv0BMEICBGDgHjx40D18+LD5SVas1O/XSsP29na3ubk5mAucZvaH1Q+AcRIgAANVX9Rv3brVHR4enugJ1v9/7969oy/9169fH8RqSK3gtHL+A2CcHEIHGKA653Hz5s0Tx8e79vf3j0KmdeWhL/X4Zn8AIEAABqa+qN+9e3emwVAhUxEyT9Osfpj9ATBeAgRgYCo+ZrHy8b76m7Ula1729vaaHrlWPmy/AhgvAQIwILVFaZop4dOqg+m1vSutDsa3RpXVD4BxEyAAA5JYobh//378glsnn3cCBGD0BAjAQNTKR+uE8NOoQ+mJx3lX66pOzf0Y+iBFAE5HgAAMRJ9br96XfqzW4DH7A2D8BAjAQNTKRMpQY8fhc4DxEyAAA5HcFpU6iD7t7I/aggXAuAkQgAFIrn50f4dBQsVH62NZ/QBYDgIEgN60Dh+s2R/ufgWwHAQIAL2oLWWtKztWPwCWhwABGID19fXok6gVh761Tj7vzP4AWCoCBGAgElEwkZi10br9qg6epwMMgPkRIAADkfwSfvHixV7/ft1lq/WuXlY/AJaLAAEYiOQ5iL4fa3d3t/l3NzY2en0uAAyLAAEYiFoJSMzBqJWWvrdgtc7+WF1dNfsDYMkIEIAB2d7e7v3JXLlypde/X2c/Wmd/bG1t9fpcABgeAQIwILUKUqsCfamtV32fNWld/ejcfhdgKQkQgIH57LPPerkjVoXNp59+2uvF1spHa4BUbCXv/AXAMAgQgIGpMxE3btyY6Zfz+lt9hc27Wm+9251i9aN1excAwyRAAAaoDolXhMzigHatfHz55ZeRw96twwcrhE4SIBUft27d6u7du3eCZwfAEAgQgIGqCPniiy9ONSdjc3Oz+/zzzyNbnWr2x+HhYdPvnuSaJvFRj1G3+Z1mtQWA4VjxXgAMV4VDnduoL+z1hbvlS/dkdaHuqJW8xe00QTBtgLwbHxO1ClKrO4mp7gDMjgABWAB156r6uXbtWre/v/9/k8Yn08bri3iFx+T35mGa2R/TRsP78dH9HSW3b98+2l7mMDvA4hAgAAtksroxtNvXVnxMYug4004+v3v37r9u7ZqsjNRWNQAWgzMgAJxaX7M/Kj6O29pVcVK/B8BiECAAnEqtQrSe/6j4aD2X0hIfE63nYwCYPwECwKn0sfoxTXy8+//U2RgAhk2AAHAqraFQ51da7n51kviYqEPpBhUCDJsAAeDE6uB53ZWrRcvqx2nio3vnUDoAwyVAADix1snn3d9DET/ktPEx4VA6wLAJEABOrDUY6uD5h2Z/zCo+Jupv1bR0AIZHgABwIrX1qnX2x4fOfsw6PiZqUnrr9jAAcgQIwIKoL/tDOmA9TTT82/DBvuJj4s6dO82RBECGAAFYAPUv+d99993RAeuhREjr7XfX19f/cfZHYnZHvVY//vijO2MBDIgAARi4+pI+ub1sHbAeQoTUc2p9Dv+0/ar+/9RB8XrNajsWAMMgQAAG7J++qNcX6ps3b8516N5phg8m4+Pdx3QoHWAYBAjAQNWX9H/7ol6rD7UqMo8IqTMVrQFSqx81gHBiHvExUasg04QTAP0QIAAD1HI4ezJ0L/2l+qSrH/OMj4l6/HmuHAEgQAAGpaLi22+/bT6c/fbt26M7Pe3s7MQuo3X4YB08nwTIEOKj+/v1refhUDrA/AgQgIGoL8V1p6sXL15M/YTu37/f/fzzz71fSK0e1BmUFkOLjwmT0gHmS4AADEB9sf/mm29ONbOiViZ++OGHXi9mmtvm1vmPocXHRG0je/DgwTCeDMCSESAAc1bxUWc5Xr9+feon8scffxyFTF9aA2R1dXXwKw0PHz50KB1gDgQIwBzVF/pataizHLPy559/Ht2md9bnHOrLeuvfrABZhG1ODqUD5AkQgDmZbE/666+/Zv4EaivX119/faotXe+bZrXg6dOnM3vcPjmUDpAnQADm4Jdfful9haBWVeqOWq2Hxj+kvqC3br86c+bMqR8vqV6fupMYABkCBCDsp59+6h49ehR50IqQurPWaVckpln96GNFp2/7+/tHgwoB6J8AAQiZTC+fx/akip7TPG5yzsi87O7uTnWXLwBORoAABFR81GHz+pf2eakIqa1f06pzJLPYxrUIahXEoXSAfgkQgJ7VF9rvv//+RAMGZ622fk07sLB18vkYVCjWeRCH0gH6I0AAejSZ8VG3xh2KCopaDWn1+PHjpfqI1IqPQ+kA/REgAD2p7VYVH7Oc8TErredB6hpmMSBx0dR1L8IcE4BFJEAAelCHmevA+RDjo6yvrzf93jJtv3pfvYcOpQPMngABmLG6m9LQ//X86tWrx/5OnYOY5va7Y2RSOsDsCRCAGaqzFUOfJ/HRRx91a2trx/5excdQV3CSaiXLoXSA2REgADNy2lkbKRsbG02PtAjXklDxUWd5AJiNFa8jwOnVLXbnOeNjGi0BUneCevbs2ZCe9lzVHJQLFy4s8SsAMDtWQABm4NWrVwvxMq6urnbnz58/9veW/ezHPxnSrZQBFpkAAVgiW1tbTRdbAwsBoA8CBGCJXLp06diLrbs+LcqKDgCLR4AALImKj3Pnzh17sWZfANAnAQKwJC5fvtx0ocs8fBCA/gkQgCVQKx8t26/M/gCgbwIEYAm0xEdn9QOAAAECsAQ2NzePvcgauPf777/7OADQKwECMHI192Ntbe3YizT7A4AEAQIwcq2Hz83+ACBBgACM3MbGxrEX+PLly+7Fixc+CgD0ToAAjNjq6urRFqzj7Ozs+BgAECFAAEZsa2ur6eJ+/fVXHwMAIgQIwIi13H53f3+/e/XqlY8BABECBGCkKj5qAOFxnjx54iMAQIwAARiplrtf1ewP268ASBIgACNUKx8t269q9sfbt299BACIESAAI9QSH2Vvb8/bD0CUAAEYoc3NzWMvqmZ/PH/+3NsPQJQAARiZmvuxtrZ27EXV9isASBMgACPTcvi8PHr0yFsPQJwAARiZjY2NYy/o4ODA7A8A5kKAAIzI6urq0Ras4+zu7nrbAZgLAQIwIltbW00XY/YHAPMiQABGxOwPAIZuxTsE8GFXrlxZiFfowoULRwMIj/PkyZPmv1krKmfP+rcqAGZHgAAcY3t7ezQv0Zs3b5pvv1urKVevXu39OQGwXPyzFsASmWb1o/V2vgAwDQECsET29vaaLra2crWcJwGAaQkQgCXx8uXL7vDwsOlirX4A0BcBArAkdnZ2mi9UgADQFwECsCRaD5/XIMO1tTUfCwB6IUAAlkDFR23BatE6zBAATkKAACyB1tWPrnGYIQCclAABGLlpZ3/UFiwA6IsAARi5io+KkBZWPwDomwABGLnW4YM1+8PdrwDomwABGLE6eL6/v990gVY/AEgQIAAj1jr5vDP7A4AQAQIwYq3br+rg+fr6uo8CAL0TIAAjdXBw0Dz7w+oHACkCBGDEWs91bGxs+BgAELHiZQYYp7W1te769etHt+CtrVh1HuTw8PD/Xevq6qrZHwDECBCAkavb625ubh791JasnZ2do9kgk+1ZW1tbPgIAxAgQgCVSKx3Xrl07+qkIqR+33wUgSYAALKkKD/EBQJpD6AAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAECMAAEAAGIECAAAECNAAACAGAECAADECBAAACBGgAAAADECBAAAiBEgAABAjAABAABiBAgAABAjQAAAgBgBAgAAxAgQAAAgRoAAAAAxAgQAAIgRIAAAQIwAAQAAYgQIAAAQI0AAAIAYAQIAAMQIEAAAIEaAAAAAMQIEAACIESAAAEDMStd1/+3lBgAAetd13f8C5ofqtHojInUAAAAASUVORK5CYII=";const u=10;function d(e){if(!e.length)return null;const t=e.map((e=>`${(0,s.AH)(e.src)} ${e.density}`)).join(", "),n=e[0],r={srcSet:t,src:(0,s.AH)(n.src)},{width:i}=n.size||{width:null};return i&&(r.width=i,r.height="auto"),r}var A={name:"ImageAsset",mixins:[a],inject:{imageLoadingStrategy:{default:null}},data:()=>({appState:o["default"].state,fallbackImageSrcSet:null,optimalWidth:null,optimalHeight:null}),computed:{allVariants:({lightVariants:e=[],darkVariants:t=[]})=>e.concat(t),defaultAttributes:({lightVariantAttributes:e,darkVariantAttributes:t})=>e||t,darkVariantAttributes:({darkVariants:e})=>d(e),lightVariantAttributes:({lightVariants:e})=>d(e),loading:({appState:e,imageLoadingStrategy:t})=>t||e.imageLoadingStrategy,preferredColorScheme:({appState:e})=>e.preferredColorScheme,prefersAuto:({preferredColorScheme:e})=>e===l.Z.auto,prefersDark:({preferredColorScheme:e})=>e===l.Z.dark,orientation:({optimalWidth:e,optimalHeight:t})=>(0,s.T8)(e,t)},props:{alt:{type:String,default:""},variants:{type:Array,required:!0},shouldCalculateOptimalWidth:{type:Boolean,default:!0}},methods:{handleImageLoadError(){this.fallbackImageSrcSet=`${c} 2x`},async calculateOptimalDimensions(){const{$refs:{img:{currentSrc:e}},allVariants:t}=this,{density:n}=t.find((({src:t})=>e.endsWith(t))),r=parseInt(n.match(/\d+/)[0],u),i=await(0,s.RY)(e),a=i.width/r,o=i.height/r;return{width:a,height:o}},async optimizeImageSize(){if(!this.defaultAttributes.width&&this.$refs.img)try{const e=await this.calculateOptimalDimensions();this.optimalWidth=e.width,this.optimalHeight=e.height}catch{console.error("Unable to calculate optimal image width")}}},mounted(){this.shouldCalculateOptimalWidth&&this.$refs.img.addEventListener("load",this.optimizeImageSize)}},p=A,h=n(1001),g=(0,h.Z)(p,r,i,!1,null,null,null),f=g.exports},2586:function(e,t,n){"use strict";n.d(t,{Z:function(){return I}});var r=function(){var e=this,t=e._self._c;return t("nav",{ref:"nav",staticClass:"nav",class:e.rootClasses,attrs:{role:"navigation"}},[t("div",{ref:"wrapper",staticClass:"nav__wrapper"},[t("div",{staticClass:"nav__background"}),e.hasOverlay?t("div",{staticClass:"nav-overlay",on:{click:e.closeNav}}):e._e(),t("div",{staticClass:"nav-content"},[e._t("pre-title",null,{className:"pre-title"},{closeNav:e.closeNav,inBreakpoint:e.inBreakpoint,currentBreakpoint:e.currentBreakpoint,isOpen:e.isOpen}),e.$slots.default?t("div",{staticClass:"nav-title"},[e._t("default")],2):e._e(),e._t("after-title"),t("div",{staticClass:"nav-menu"},[t("a",{ref:"axToggle",staticClass:"nav-ax-toggle",attrs:{href:"#",role:"button"},on:{click:function(t){return t.preventDefault(),e.toggleNav.apply(null,arguments)}}},[t("span",{staticClass:"visuallyhidden"},[e.isOpen?[e._v(" "+e._s(e.$t("documentation.nav.close-menu"))+" ")]:[e._v(" "+e._s(e.$t("documentation.nav.open-menu"))+" ")]],2)]),t("div",{ref:"tray",staticClass:"nav-menu-tray",on:{transitionend:function(t){return t.target!==t.currentTarget?null:e.onTransitionEnd.apply(null,arguments)},click:e.handleTrayClick}},[e._t("tray",(function(){return[t("NavMenuItems",[e._t("menu-items")],2)]}),{closeNav:e.closeNav})],2)]),e.showActions?t("div",{staticClass:"nav-actions"},[t("a",{ref:"toggle",staticClass:"nav-menucta",attrs:{href:"#",tabindex:"-1","aria-hidden":"true"},on:{click:function(t){return t.preventDefault(),e.toggleNav.apply(null,arguments)}}},[t("span",{staticClass:"nav-menucta-chevron"})])]):e._e()],2),e._t("after-content")],2),t("BreakpointEmitter",{attrs:{scope:e.BreakpointScopes.nav},on:{change:e.onBreakpointChange}})],1)},i=[],s=n(9146),a=n(2853),o=n(7188),l=n(9652),c=n(1716),u=n(5381),d=n(1147),A=n(5657);const{noClose:p}=c.MenuLinkModifierClasses,{BreakpointName:h,BreakpointScopes:g}=o["default"].constants,f=8,m={isDark:"theme-dark",isOpen:"nav--is-open",inBreakpoint:"nav--in-breakpoint-range",isTransitioning:"nav--is-transitioning",isSticking:"nav--is-sticking",hasSolidBackground:"nav--solid-background",hasNoBorder:"nav--noborder",hasFullWidthBorder:"nav--fullwidth-border",isWideFormat:"nav--is-wide-format",noBackgroundTransition:"nav--no-bg-transition"};var v={name:"NavBase",components:{NavMenuItems:a.Z,BreakpointEmitter:o["default"]},constants:{NavStateClasses:m,NoBGTransitionFrames:f},props:{breakpoint:{type:String,default:h.small},hasOverlay:{type:Boolean,default:!0},hasSolidBackground:{type:Boolean,default:!1},hasNoBorder:{type:Boolean,default:!1},hasFullWidthBorder:{type:Boolean,default:!1},isDark:{type:Boolean,default:!1},isWideFormat:{type:Boolean,default:!1},showActions:{type:Boolean,default:!0}},mixins:[s["default"]],data(){return{isOpen:!1,isTransitioning:!1,isSticking:!1,noBackgroundTransition:!0,currentBreakpoint:h.large}},computed:{BreakpointScopes:()=>g,inBreakpoint:({currentBreakpoint:e,breakpoint:t})=>!(0,u.fr)(e,t),rootClasses:({isOpen:e,inBreakpoint:t,isTransitioning:n,isSticking:r,hasSolidBackground:i,hasNoBorder:s,hasFullWidthBorder:a,isDark:o,isWideFormat:l,noBackgroundTransition:c})=>({[m.isDark]:o,[m.isOpen]:e,[m.inBreakpoint]:t,[m.isTransitioning]:n,[m.isSticking]:r,[m.hasSolidBackground]:i,[m.hasNoBorder]:s,[m.hasFullWidthBorder]:a,[m.isWideFormat]:l,[m.noBackgroundTransition]:c})},watch:{isOpen(e){this.$emit("change",e),e?this.onExpand():this.onClose()}},async mounted(){window.addEventListener("keydown",this.onEscape),window.addEventListener("popstate",this.closeNav),window.addEventListener("orientationchange",this.closeNav),document.addEventListener("click",this.handleClickOutside),this.handleFlashOnMount(),await this.$nextTick()},beforeDestroy(){window.removeEventListener("keydown",this.onEscape),window.removeEventListener("popstate",this.closeNav),window.removeEventListener("orientationchange",this.closeNav),document.removeEventListener("click",this.handleClickOutside),this.isOpen&&this.toggleScrollLock(!1)},methods:{getIntersectionTargets(){return[document.getElementById(c.EA)||this.$el]},toggleNav(){this.isOpen=!this.isOpen,this.isTransitioning=!0},closeNav(){const e=this.isOpen;return this.isOpen=!1,this.resolveOnceTransitionsEnd(e)},resolveOnceTransitionsEnd(e){return e&&this.inBreakpoint?(this.isTransitioning=!0,new Promise((e=>{const t=this.$watch("isTransitioning",(()=>{e(),t()}))}))):Promise.resolve()},async onTransitionEnd({propertyName:e}){"max-height"===e&&(this.$emit("changed",this.isOpen),this.isTransitioning=!1,this.isOpen?(this.$emit("opened"),this.toggleScrollLock(!0)):this.$emit("closed"))},onBreakpointChange(e){this.currentBreakpoint=e,this.inBreakpoint||this.closeNav()},onIntersect({intersectionRatio:e}){window.scrollY<0||(this.isSticking=1!==e)},onEscape({key:e}){"Escape"===e&&this.isOpen&&(this.closeNav(),this.$refs.axToggle.focus())},handleTrayClick({target:e}){e.href&&!e.classList.contains(p)&&this.closeNav()},handleClickOutside({target:e}){this.$refs.nav.contains(e)||this.closeNav()},toggleScrollLock(e){e?l.Z.lockScroll(this.$refs.tray):l.Z.unlockScroll(this.$refs.tray)},onExpand(){this.$emit("open"),d.Z.hide(this.$refs.wrapper),document.activeElement===this.$refs.toggle&&document.activeElement.blur()},onClose(){this.$emit("close"),this.toggleScrollLock(!1),d.Z.show(this.$refs.wrapper)},async handleFlashOnMount(){await(0,A.J)(f),this.noBackgroundTransition=!1}}},b=v,y=n(1001),C=(0,y.Z)(b,r,i,!1,null,"40ae4336",null),I=C.exports},535:function(e,t,n){"use strict";n.d(t,{Z:function(){return c}});var r=function(){var e=this,t=e._self._c;return t("li",{staticClass:"nav-menu-item",class:{"nav-menu-item--animated":e.animate}},[e._t("default")],2)},i=[],s={name:"NavMenuItemBase",props:{animate:{type:Boolean,default:!0}}},a=s,o=n(1001),l=(0,o.Z)(a,r,i,!1,null,"296e4e0c",null),c=l.exports},2853:function(e,t,n){"use strict";n.d(t,{Z:function(){return c}});var r=function(){var e=this,t=e._self._c;return t("ul",{staticClass:"nav-menu-items"},[e._t("default")],2)},i=[],s={name:"NavMenuItems"},a=s,o=n(1001),l=(0,o.Z)(a,r,i,!1,null,"a101abb4",null),c=l.exports},5921:function(e,t,n){"use strict";n.d(t,{Z:function(){return le}});var r=function(){var e=this,t=e._self._c;return t("div",{staticClass:"TopicTypeIcon",style:e.styles},[e.imageOverride?t("OverridableAsset",{staticClass:"icon-inline",attrs:{imageOverride:e.imageOverride,shouldCalculateOptimalWidth:e.shouldCalculateOptimalWidth}}):t(e.icon,e._b({tag:"component",staticClass:"icon-inline"},"component",e.iconProps,!1))],1)},i=[],s=n(8633),a=n(9001),o=n(5692),l=n(8638),c=function(){var e=this,t=e._self._c;return t("SVGIcon",{attrs:{viewBox:"0 0 14 14",height:"14",themeId:"topic-func"}},[t("path",{attrs:{d:"M13 1v12h-12v-12zM12.077 1.923h-10.154v10.154h10.154z"}}),t("path",{attrs:{d:"M5.191 9.529c0.044 0.002 0.089 0.004 0.133 0.004 0.108 0 0.196-0.025 0.262-0.074s0.122-0.113 0.166-0.188c0.044-0.077 0.078-0.159 0.103-0.247s0.049-0.173 0.074-0.251l0.598-2.186h-0.709l0.207-0.702h0.702l0.288-1.086c0.083-0.384 0.256-0.667 0.517-0.849s0.591-0.273 0.99-0.273c0.108 0 0.212 0.007 0.314 0.022s0.203 0.027 0.306 0.037l-0.207 0.761c-0.054-0.006-0.106-0.011-0.155-0.018s-0.102-0.011-0.155-0.011c-0.108 0-0.196 0.016-0.262 0.048s-0.122 0.075-0.166 0.129-0.080 0.115-0.107 0.185c-0.028 0.068-0.055 0.14-0.085 0.214l-0.222 0.842h0.768l-0.192 0.702h-0.783l-0.628 2.319c-0.059 0.222-0.129 0.419-0.21 0.594s-0.182 0.322-0.303 0.443-0.269 0.214-0.443 0.281-0.385 0.1-0.631 0.1c-0.084 0-0.168-0.004-0.251-0.011s-0.168-0.014-0.251-0.018l0.207-0.768c0.040 0 0.081 0.001 0.126 0.004z"}})])},u=[],d=n(9742),A={name:"TopicFuncIcon",components:{SVGIcon:d.Z}},p=A,h=n(1001),g=(0,h.Z)(p,c,u,!1,null,null,null),f=g.exports,m=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"collection-icon",attrs:{viewBox:"0 0 14 14",themeId:"collection"}},[t("path",{attrs:{d:"m1 1v12h12v-12zm11 11h-10v-10h10z"}}),t("path",{attrs:{d:"m3 4h8v1h-8zm0 2.5h8v1h-8zm0 2.5h8v1h-8z"}}),t("path",{attrs:{d:"m3 4h8v1h-8z"}}),t("path",{attrs:{d:"m3 6.5h8v1h-8z"}}),t("path",{attrs:{d:"m3 9h8v1h-8z"}})])},v=[],b={name:"CollectionIcon",components:{SVGIcon:d.Z}},y=b,C=(0,h.Z)(y,m,v,!1,null,null,null),I=C.exports,w=function(){var e=this,t=e._self._c;return t("SVGIcon",{attrs:{viewBox:"0 0 14 14",height:"14",themeId:"topic-func-op"}},[t("path",{attrs:{d:"M13 13h-12v-12h12zM1.923 12.077h10.154v-10.154h-10.154z"}}),t("path",{attrs:{d:"M5.098 4.968v-1.477h-0.738v1.477h-1.477v0.738h1.477v1.477h0.738v-1.477h1.477v-0.738z"}}),t("path",{attrs:{d:"M8.030 4.807l-2.031 5.538h0.831l2.031-5.538z"}}),t("path",{attrs:{d:"M8.894 8.805v0.923h2.215v-0.923z"}})])},E=[],B={name:"TopicFuncOpIcon",components:{SVGIcon:d.Z}},x=B,k=(0,h.Z)(x,w,E,!1,null,null,null),_=k.exports,S=n(7775),T=function(){var e=this,t=e._self._c;return t("SVGIcon",{attrs:{viewBox:"0 0 14 14",height:"14",themeId:"topic-subscript"}},[t("path",{attrs:{d:"M13 13h-12v-12h12zM1.923 12.077h10.154v-10.154h-10.154z"}}),t("path",{attrs:{d:"M4.133 3.633v6.738h1.938v-0.831h-0.923v-5.077h0.923v-0.831z"}}),t("path",{attrs:{d:"M9.856 10.371v-6.738h-1.938v0.831h0.923v5.077h-0.923v0.831z"}})])},L=[],P={name:"TopicSubscriptIcon",components:{SVGIcon:d.Z}},Q=P,M=(0,h.Z)(Q,T,L,!1,null,null,null),Z=M.exports,D=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"two-letter-icon",attrs:{width:"16px",height:"16px",viewBox:"0 0 16 16",themeId:"two-letter"}},[t("g",{attrs:{stroke:"none","stroke-width":"1",fill:"none","fill-rule":"evenodd"}},[t("g",{attrs:{transform:"translate(1.000000, 1.000000)"}},[t("rect",{attrs:{stroke:"currentColor",x:"0.5",y:"0.5",width:"13",height:"13"}}),t("text",{attrs:{"font-size":"8","font-weight":"bold",fill:"currentColor"}},[t("tspan",{attrs:{x:"8.2",y:"11"}},[e._v(e._s(e.second))])]),t("text",{attrs:{"font-size":"11","font-weight":"bold",fill:"currentColor"}},[t("tspan",{attrs:{x:"1.7",y:"11"}},[e._v(e._s(e.first))])])])])])},O=[],N={name:"TwoLetterSymbolIcon",components:{SVGIcon:d.Z},props:{first:{type:String,required:!0},second:{type:String,required:!0}}},R=N,j=(0,h.Z)(R,D,O,!1,null,null,null),G=j.exports,V=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"single-letter-icon",attrs:{width:"16px",height:"16px",viewBox:"0 0 16 16",themeId:"single-letter"}},[t("g",{attrs:{stroke:"none","stroke-width":"1",fill:"none","fill-rule":"evenodd"}},[t("rect",{attrs:{stroke:"currentColor",x:"1",y:"1",width:"14",height:"14"}}),t("text",{attrs:{"font-size":"11","font-weight":"bold",fill:"currentColor",x:"49%",y:"12","text-anchor":"middle"}},[t("tspan",[e._v(e._s(e.symbol))])])])])},$=[],z={name:"SingleLetterSymbolIcon",components:{SVGIcon:d.Z},props:{symbol:{type:String,required:!0}}},q=z,H=(0,h.Z)(q,V,$,!1,null,null,null),W=H.exports,F=n(5629),U=n(1869),Y=function(){var e=this,t=e._self._c;return e.shouldUseAsset?t("ImageAsset",e._b({},"ImageAsset",{variants:e.variants,loading:null,shouldCalculateOptimalWidth:e.shouldCalculateOptimalWidth,alt:e.alt},!1)):t("SVGIcon",{attrs:{"icon-url":e.iconUrl,themeId:e.themeId}})},X=[],K=n(6769),J={name:"OverridableAsset",components:{SVGIcon:d.Z,ImageAsset:K.Z},props:{imageOverride:{type:Object,default:null},shouldCalculateOptimalWidth:{type:Boolean,default:!0}},computed:{variants:({imageOverride:e})=>e?e.variants:[],alt:({imageOverride:e})=>e.alt,firstVariant:({variants:e})=>e[0],iconUrl:({firstVariant:e})=>e&&e.url,themeId:({firstVariant:e})=>e&&e.svgID,isSameOrigin:({iconUrl:e,sameOrigin:t})=>t(e),shouldUseAsset:({isSameOrigin:e,themeId:t})=>!e||!t},methods:{sameOrigin(e){if(!e)return!1;const t=new URL(e,window.location),n=new URL(window.location);return t.origin===n.origin}}},ee=J,te=(0,h.Z)(ee,Y,X,!1,null,null,null),ne=te.exports;const re={[F.t.article]:o.Z,[F.t.associatedtype]:I,[F.t.buildSetting]:I,[F.t["class"]]:W,[F.t.collection]:I,[F.t.dictionarySymbol]:W,[F.t.container]:I,[F.t["enum"]]:W,[F.t.extension]:G,[F.t.func]:f,[F.t.op]:_,[F.t.httpRequest]:W,[F.t.languageGroup]:I,[F.t.learn]:s.Z,[F.t.method]:W,[F.t.macro]:W,[F.t.module]:a.Z,[F.t.namespace]:W,[F.t.overview]:s.Z,[F.t.protocol]:G,[F.t.property]:W,[F.t.propertyListKey]:W,[F.t.resources]:s.Z,[F.t.sampleCode]:S.Z,[F.t.struct]:W,[F.t.subscript]:Z,[F.t.symbol]:I,[F.t.tutorial]:l.Z,[F.t.typealias]:W,[F.t.union]:W,[F.t["var"]]:W},ie={[F.t["class"]]:{symbol:"C"},[F.t.dictionarySymbol]:{symbol:"O"},[F.t["enum"]]:{symbol:"E"},[F.t.extension]:{first:"E",second:"x"},[F.t.httpRequest]:{symbol:"E"},[F.t.method]:{symbol:"M"},[F.t.macro]:{symbol:"#"},[F.t.namespace]:{symbol:"N"},[F.t.protocol]:{first:"P",second:"r"},[F.t.property]:{symbol:"P"},[F.t.propertyListKey]:{symbol:"K"},[F.t.struct]:{symbol:"S"},[F.t.typealias]:{symbol:"T"},[F.t.union]:{symbol:"U"},[F.t["var"]]:{symbol:"V"}};var se={name:"TopicTypeIcon",components:{OverridableAsset:ne,SVGIcon:d.Z,SingleLetterSymbolIcon:W},constants:{TopicTypeIcons:re,TopicTypeProps:ie},props:{type:{type:String,required:!0},withColors:{type:Boolean,default:!1},imageOverride:{type:Object,default:null},shouldCalculateOptimalWidth:{type:Boolean,default:!0}},computed:{normalisedType:({type:e})=>F.$[e]||e,icon:({normalisedType:e})=>re[e]||I,iconProps:({normalisedType:e})=>ie[e]||{},color:({normalisedType:e})=>U.g[e],styles:({color:e,withColors:t})=>t&&e?{"--icon-color":`var(--color-type-icon-${e})`}:{}}},ae=se,oe=(0,h.Z)(ae,r,i,!1,null,"3b1b4787",null),le=oe.exports},352:function(e,t,n){"use strict";n.d(t,{Z:function(){return c}});var r,i,s={functional:!0,name:"WordBreak",render(e,{props:t,slots:n,data:r}){const i=n().default||[],s=i.filter((e=>e.text&&!e.tag));if(0===s.length||s.length!==i.length)return e(t.tag,r,i);const a=s.map((({text:e})=>e)).join(),o=[];let l=null,c=0;while(null!==(l=t.safeBoundaryPattern.exec(a))){const t=l.index+1;o.push(a.slice(c,t)),o.push(e("wbr",{key:l.index})),c=t}return o.push(a.slice(c,a.length)),e(t.tag,r,o)},props:{safeBoundaryPattern:{type:RegExp,default:()=>/([a-z](?=[A-Z])|(:)\w|\w(?=[._]\w))/g},tag:{type:String,default:()=>"span"}}},a=s,o=n(1001),l=(0,o.Z)(a,r,i,!1,null,null,null),c=l.exports},2122:function(e,t,n){var r={"./bash.js":[8780,393],"./c.js":[612,546],"./cpp.js":[6248,621],"./css.js":[5064,864],"./diff.js":[7731,213],"./http.js":[8937,878],"./java.js":[8257,788],"./javascript.js":[978,814],"./json.js":[14,82],"./llvm.js":[4972,133],"./markdown.js":[1312,113],"./objectivec.js":[2446,637],"./perl.js":[2482,645],"./php.js":[2656,596],"./python.js":[8245,435],"./ruby.js":[7905,623],"./scss.js":[1062,392],"./shell.js":[7874,176],"./swift.js":[7690,527],"./xml.js":[4610,490]};function i(e){if(!n.o(r,e))return Promise.resolve().then((function(){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}));var t=r[e],i=t[0];return n.e(t[1]).then((function(){return n.t(i,23)}))}i.keys=function(){return Object.keys(r)},i.id=2122,e.exports=i},9426:function(e,t,n){"use strict";n.d(t,{Ag:function(){return s},UG:function(){return i},yf:function(){return r}});const r={added:"added",modified:"modified",deprecated:"deprecated"},i=[r.modified,r.added,r.deprecated],s={[r.modified]:"change-type.modified",[r.added]:"change-type.added",[r.deprecated]:"change-type.deprecated"}},1869:function(e,t,n){"use strict";n.d(t,{c:function(){return s},g:function(){return a}});var r=n(5629),i=n(7192);const s={blue:"blue",teal:"teal",orange:"orange",purple:"purple",green:"green",sky:"sky",pink:"pink"},a={[r.t.article]:s.teal,[r.t.init]:s.blue,[r.t["case"]]:s.orange,[r.t["class"]]:s.purple,[r.t.collection]:s.pink,[i.L.collectionGroup]:s.teal,[r.t.dictionarySymbol]:s.purple,[r.t["enum"]]:s.orange,[r.t.extension]:s.orange,[r.t.func]:s.green,[r.t.op]:s.green,[r.t.httpRequest]:s.green,[r.t.module]:s.sky,[r.t.method]:s.blue,[r.t.macro]:s.pink,[r.t.protocol]:s.purple,[r.t.property]:s.teal,[r.t.propertyListKey]:s.green,[r.t.propertyListKeyReference]:s.green,[r.t.sampleCode]:s.purple,[r.t.struct]:s.purple,[r.t.subscript]:s.blue,[r.t.typealias]:s.orange,[r.t.union]:s.purple,[r.t["var"]]:s.purple}},3078:function(e,t){"use strict";t["Z"]={objectiveC:{name:"Objective-C",key:{api:"occ",url:"objc"}},swift:{name:"Swift",key:{api:"swift",url:"swift"}}}},3946:function(e,t,n){"use strict";n.d(t,{o:function(){return r}});const r={list:"list",compactGrid:"compactGrid",detailedGrid:"detailedGrid",hidden:"hidden"}},5629:function(e,t,n){"use strict";n.d(t,{$:function(){return i},t:function(){return r}});const r={article:"article",associatedtype:"associatedtype",buildSetting:"buildSetting",case:"case",collection:"collection",class:"class",container:"container",dictionarySymbol:"dictionarySymbol",enum:"enum",extension:"extension",func:"func",groupMarker:"groupMarker",httpRequest:"httpRequest",init:"init",languageGroup:"languageGroup",learn:"learn",macro:"macro",method:"method",module:"module",namespace:"namespace",op:"op",overview:"overview",project:"project",property:"property",propertyListKey:"propertyListKey",propertyListKeyReference:"propertyListKeyReference",protocol:"protocol",resources:"resources",root:"root",sampleCode:"sampleCode",section:"section",struct:"struct",subscript:"subscript",symbol:"symbol",tutorial:"tutorial",typealias:"typealias",union:"union",var:"var"},i={[r.init]:r.method,[r.case]:r.enum,[r.propertyListKeyReference]:r.propertyListKey,[r.project]:r.tutorial}},7192:function(e,t,n){"use strict";n.d(t,{L:function(){return r}});const r={article:"article",codeListing:"codeListing",collection:"collection",collectionGroup:"collectionGroup",containerSymbol:"containerSymbol",devLink:"devLink",dictionarySymbol:"dictionarySymbol",generic:"generic",link:"link",media:"media",pseudoCollection:"pseudoCollection",pseudoSymbol:"pseudoSymbol",restRequestSymbol:"restRequestSymbol",sampleCode:"sampleCode",symbol:"symbol",table:"table",learn:"learn",overview:"overview",project:"project",tutorial:"tutorial",resources:"resources"}},1789:function(e,t){"use strict";t["Z"]={inject:{performanceMetricsEnabled:{default:!1},isTargetIDE:{default:!1}},methods:{newContentMounted(){let e;this.performanceMetricsEnabled&&(e=Math.round(window.performance.now()),window.renderedTimes||(window.renderedTimes=[]),window.renderedTimes.push(e)),this.$bridge.send({type:"rendered",data:{time:e}})},handleContentUpdateFromBridge(e){this.topicData=e}}}},2974:function(e,t,n){"use strict";var r=n(3465),i=n(3208),s=n(2449),a=n(9519);t["Z"]={methods:{extractFirstParagraphText(e=[]){const{references:t={}}=this,n=a["default"].computed.plaintext.bind({...a["default"].methods,content:e,references:t})();return(0,i.id)(n)}},computed:{pagePath:({$route:{path:e="/"}={}})=>e,pageURL:({pagePath:e="/"})=>(0,s.HH)(e),disableMetadata:()=>!1},mounted(){this.disableMetadata||(0,r.X)({title:this.pageTitle,description:this.pageDescription,url:this.pageURL,currentLocale:this.$i18n.locale})}}},9146:function(e,t,n){"use strict";const r={up:"up",down:"down"};t["default"]={constants:{IntersectionDirections:r},data(){return{intersectionObserver:null,intersectionPreviousScrollY:0,intersectionScrollDirection:r.down}},computed:{intersectionThreshold(){const e=[];for(let t=0;t<=1;t+=.01)e.push(t);return e},intersectionRoot(){return null},intersectionRootMargin(){return"0px 0px 0px 0px"},intersectionObserverOptions(){return{root:this.intersectionRoot,rootMargin:this.intersectionRootMargin,threshold:this.intersectionThreshold}}},async mounted(){await n.e(337).then(n.t.bind(n,6337,23)),this.intersectionObserver=new IntersectionObserver((e=>{this.detectIntersectionScrollDirection();const t=this.onIntersect;t?e.forEach(t):console.warn("onIntersect not implemented")}),this.intersectionObserverOptions),this.getIntersectionTargets().forEach((e=>{this.intersectionObserver.observe(e)}))},beforeDestroy(){this.intersectionObserver&&this.intersectionObserver.disconnect()},methods:{getIntersectionTargets(){return[this.$el]},detectIntersectionScrollDirection(){window.scrollYthis.intersectionPreviousScrollY&&(this.intersectionScrollDirection=r.up),this.intersectionPreviousScrollY=window.scrollY}}}},5184:function(e,t,n){"use strict";var r=n(4030),i=n(1265),s=n(3704);function a(e){return new Promise(((t,n)=>{e.complete?t():(e.addEventListener("load",t,{once:!0}),e.addEventListener("error",n,{once:!0}))}))}function o(){return Promise.allSettled([...document.getElementsByTagName("img")].map(a))}t["Z"]={mixins:[s.Z],mounted(){this.scrollToElementIfAnchorPresent()},updated(){this.scrollToElementIfAnchorPresent()},methods:{async scrollToElementIfAnchorPresent(){const{hash:e}=this.$route;if(!e)return;const{imageLoadingStrategy:t}=r["default"].state;r["default"].setImageLoadingStrategy(i.Z.eager),await this.$nextTick(),await o(),this.scrollToElement(e),r["default"].setImageLoadingStrategy(t)}}}},5953:function(e,t){"use strict";t["Z"]={inject:{store:{default:()=>({state:{references:{}},setReferences(){},reset(){}})}},computed:{references:({store:e})=>e.state.references}}},3704:function(e,t,n){"use strict";var r=n(5657);t["Z"]={methods:{async scrollToElement(e){await(0,r.J)(8);const t=this.$router.resolve({hash:e}),{selector:n,offset:i}=await this.$router.options.scrollBehavior(t.route),s=document.querySelector(n);return s?(s.scrollIntoView(),window.scrollY+window.innerHeight({[i.yf.modified]:0,[i.yf.added]:0,[i.yf.deprecated]:0});var a={state:{apiChanges:null,apiChangesCounts:s(),selectedAPIChangesVersion:null},setAPIChanges(e){this.state.apiChanges=e},setSelectedAPIChangesVersion(e){this.state.selectedAPIChangesVersion=e},resetApiChanges(){this.state.apiChanges=null,this.state.apiChangesCounts=s()},async updateApiChangesCounts(){await r["default"].nextTick(),Object.keys(this.state.apiChangesCounts).forEach((e=>{this.state.apiChangesCounts[e]=this.countChangeType(e)}))},countChangeType(e){if(document&&document.querySelectorAll){const t=`.changed-${e}:not(.changed-total)`;return document.querySelectorAll(t).length}return 0}},o={state:{onThisPageSections:[],currentPageAnchor:null},resetPageSections(){this.state.onThisPageSections=[],this.state.currentPageAnchor=null},addOnThisPageSection(e,{i18n:t=!0}={}){this.state.onThisPageSections.push({...e,i18n:t})},setCurrentPageSection(e){const t=this.state.onThisPageSections.findIndex((({anchor:t})=>t===e));-1!==t&&(this.state.currentPageAnchor=e)}},l=n(5394);const{state:c,...u}=a,{state:d,...A}=o;var p={state:{preferredLanguage:l.Z.preferredLanguage,contentWidth:0,...c,...d,references:{}},reset(){this.state.preferredLanguage=l.Z.preferredLanguage,this.state.references={},this.resetApiChanges()},setPreferredLanguage(e){this.state.preferredLanguage=e,l.Z.preferredLanguage=this.state.preferredLanguage},setContentWidth(e){this.state.contentWidth=e},setReferences(e){this.state.references=e},...u,...A}},7486:function(e,t,n){"use strict";const r=["input","select","textarea","button","optgroup","option","menuitem","fieldset","object","a[href]","*[tabindex]","*[contenteditable]"],i=r.join(",");t["ZP"]={getTabbableElements(e){const t=e.querySelectorAll(i),n=t.length;let r;const s=[];for(r=0;r=0},isFocusableElement(e){const t=e.nodeName.toLowerCase(),n=r.includes(t);return!("a"!==t||!e.getAttribute("href"))||(n?!e.disabled:"true"===e.getAttribute("contenteditable")||!Number.isNaN(parseFloat(e.getAttribute("tabindex"))))}}},5654:function(e,t,n){"use strict";n.d(t,{Md:function(){return a},Xy:function(){return i},Z$:function(){return r}});const r=e=>e[e.length-1],i=(e,t)=>JSON.stringify(e)===JSON.stringify(t);function*s(e,t){for(let n=0;n{o(e,s),o(e,a),e.setAttribute(s,"true"),e.setAttribute(a,"-1");const t=r.ZP.getTabbableElements(e);let n=t.length-1;while(n>=0)o(t[n],a),t[n].setAttribute(a,"-1"),n-=1},d=e=>{l(e,s),l(e,a);const t=e.querySelectorAll(`[${i+a}]`);let n=t.length-1;while(n>=0)l(t[n],a),n-=1};t["Z"]={hide(e){c(e,u)},show(e){c(e,d)}}},8841:function(e,t,n){"use strict";n.d(t,{d9:function(){return h},k_:function(){return p},Ek:function(){return d},LR:function(){return g},Us:function(){return A}});var r=n(5947),i=n(2449),s=n(1944);class a extends Error{constructor({location:e,response:t}){super("Request redirected"),this.location=e,this.response=t}}class o extends Error{constructor(e){super("Unable to fetch data"),this.route=e}}async function l(e,t={},n={}){function r(e){return("ide"!=={NODE_ENV:"production",VUE_APP_TITLE:"Documentation",BASE_URL:"{{BASE_PATH}}/"}.VUE_APP_TARGET||0!==e.status)&&!e.ok}const o=(0,i.WN)(e),l=(0,i.Ex)(t);l&&(o.search=l);const c=await fetch(o.href,n);if(r(c))throw c;if(c.redirected)throw new a({location:c.url,response:c});const u=await c.json();return(0,s.ZP)(u.schemaVersion),u}function c(e){const t=e.replace(/\/$/,"");return`${(0,r.AH)(["/data",t])}.json`}function u(e){const{pathname:t,search:n}=new URL(e),r=/\/data(\/.*).json$/,i=r.exec(t);return i?i[1]+n:t+n}async function d(e,t,n){const r=c(e.path);let i;try{i=await l(r,e.query)}catch(s){if("ide"==={NODE_ENV:"production",VUE_APP_TITLE:"Documentation",BASE_URL:"{{BASE_PATH}}/"}.VUE_APP_TARGET)throw console.error(s),!1;if(s instanceof a)throw u(s.location);s.status&&404===s.status?n({name:"not-found",params:[e.path]}):n(new o(e))}return i}function A(e,t){return!(0,i.Lp)(e,t)}async function p(e,t={}){const n=c(e);return l(n,{},t)}function h(e){return JSON.parse(JSON.stringify(e))}async function g({slug:e}){const t=(0,i.WN)(["/index/",e,"index.json"]);return l(t)}},1944:function(e,t,n){"use strict";n.d(t,{W1:function(){return i},ZP:function(){return d},n4:function(){return a}});const r={major:0,minor:3,patch:0};function i({major:e,minor:t,patch:n}){return[e,t,n].join(".")}function s(e){const[t=0,n=0,r=0]=e.split(".");return[Number(t),Number(n),Number(r)]}function a(e,t){const n=s(e),r=s(t);for(let i=0;ir[i])return 1;if(n[i]`[Swift-DocC-Render] The render node version for this page (${e}) has a different major version component than Swift-DocC-Render supports (${o}). Compatibility is not guaranteed.`;function u(e){const{major:t,minor:n}=e,{major:s,minor:a}=r;return t!==s?c(i(e)):n>a?l(i(e)):""}function d(e){if(!e)return;const t=u(e);t&&console.warn(t)}},9652:function(e,t,n){"use strict";n.d(t,{n:function(){return a}});let r=!1,i=-1,s=0;const a="data-scroll-lock-disable",o=()=>window.navigator&&window.navigator.platform&&(/iP(ad|hone|od)/.test(window.navigator.platform)||"MacIntel"===window.navigator.platform&&window.navigator.maxTouchPoints>1);function l(e){e.touches.length>1||e.preventDefault()}const c=e=>!!e&&e.scrollHeight-e.scrollTop<=e.clientHeight;function u(){s=document.body.getBoundingClientRect().top,document.body.style.overflow="hidden scroll",document.body.style.top=`${s}px`,document.body.style.position="fixed",document.body.style.width="100%"}function d(e){e&&(e.ontouchstart=null,e.ontouchmove=null),document.removeEventListener("touchmove",l)}function A(e,t){const n=e.targetTouches[0].clientY-i,r=e.target.closest(`[${a}]`)||t;return 0===r.scrollTop&&n>0||c(r)&&n<0?l(e):(e.stopPropagation(),!0)}function p(e){document.addEventListener("touchmove",l,{passive:!1}),e&&(e.ontouchstart=e=>{1===e.targetTouches.length&&(i=e.targetTouches[0].clientY)},e.ontouchmove=t=>{1===t.targetTouches.length&&A(t,e)})}t["Z"]={lockScroll(e){r||(o()?p(e):u(),r=!0)},unlockScroll(e){r&&(o()?d(e):(document.body.style.removeProperty("overflow"),document.body.style.removeProperty("top"),document.body.style.removeProperty("position"),document.body.style.removeProperty("width"),window.scrollTo(0,Math.abs(s))),r=!1)}}},3685:function(e,t,n){var r={"./markdown":[2003,642],"./markdown.js":[2003,642],"./swift":[7467,217],"./swift.js":[7467,217]};function i(e){if(!n.o(r,e))return Promise.resolve().then((function(){var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}));var t=r[e],i=t[0];return n.e(t[1]).then((function(){return n(i)}))}i.keys=function(){return Object.keys(r)},i.id=3685,e.exports=i},3390:function(e){var t={exports:{}};function n(e){return e instanceof Map?e.clear=e.delete=e.set=function(){throw new Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=function(){throw new Error("set is read-only")}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach((function(t){var r=e[t];"object"!=typeof r||Object.isFrozen(r)||n(r)})),e}t.exports=n,t.exports.default=n;var r=t.exports;class i{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function s(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function a(e,...t){const n=Object.create(null);for(const r in e)n[r]=e[r];return t.forEach((function(e){for(const t in e)n[t]=e[t]})),n}const o="",l=e=>!!e.kind,c=(e,{prefix:t})=>{if(e.includes(".")){const n=e.split(".");return[`${t}${n.shift()}`,...n.map(((e,t)=>`${e}${"_".repeat(t+1)}`))].join(" ")}return`${t}${e}`};class u{constructor(e,t){this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){this.buffer+=s(e)}openNode(e){if(!l(e))return;let t=e.kind;t=e.sublanguage?`language-${t}`:c(t,{prefix:this.classPrefix}),this.span(t)}closeNode(e){l(e)&&(this.buffer+=o)}value(){return this.buffer}span(e){this.buffer+=``}}class d{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const t={kind:e,children:[]};this.add(t),this.stack.push(t)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){while(this.closeNode());}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){return"string"===typeof t?e.addText(t):t.children&&(e.openNode(t),t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){"string"!==typeof e&&e.children&&(e.children.every((e=>"string"===typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{d._collapse(e)})))}}class A extends d{constructor(e){super(),this.options=e}addKeyword(e,t){""!==e&&(this.openNode(t),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,t){const n=e.root;n.kind=t,n.sublanguage=!0,this.add(n)}toHTML(){const e=new u(this,this.options);return e.value()}finalize(){return!0}}function p(e){return e?"string"===typeof e?e:e.source:null}function h(e){return m("(?=",e,")")}function g(e){return m("(?:",e,")*")}function f(e){return m("(?:",e,")?")}function m(...e){const t=e.map((e=>p(e))).join("");return t}function v(e){const t=e[e.length-1];return"object"===typeof t&&t.constructor===Object?(e.splice(e.length-1,1),t):{}}function b(...e){const t=v(e),n="("+(t.capture?"":"?:")+e.map((e=>p(e))).join("|")+")";return n}function y(e){return new RegExp(e.toString()+"|").exec("").length-1}function C(e,t){const n=e&&e.exec(t);return n&&0===n.index}const I=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;function w(e,{joinWith:t}){let n=0;return e.map((e=>{n+=1;const t=n;let r=p(e),i="";while(r.length>0){const e=I.exec(r);if(!e){i+=r;break}i+=r.substring(0,e.index),r=r.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?i+="\\"+String(Number(e[1])+t):(i+=e[0],"("===e[0]&&n++)}return i})).map((e=>`(${e})`)).join(t)}const E=/\b\B/,B="[a-zA-Z]\\w*",x="[a-zA-Z_]\\w*",k="\\b\\d+(\\.\\d+)?",_="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",S="\\b(0b[01]+)",T="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",L=(e={})=>{const t=/^#![ ]*\//;return e.binary&&(e.begin=m(t,/.*\b/,e.binary,/\b.*/)),a({scope:"meta",begin:t,end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)},P={begin:"\\\\[\\s\\S]",relevance:0},Q={scope:"string",begin:"'",end:"'",illegal:"\\n",contains:[P]},M={scope:"string",begin:'"',end:'"',illegal:"\\n",contains:[P]},Z={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},D=function(e,t,n={}){const r=a({scope:"comment",begin:e,end:t,contains:[]},n);r.contains.push({scope:"doctag",begin:"[ ]*(?=(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):)",end:/(TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):/,excludeBegin:!0,relevance:0});const i=b("I","a","is","so","us","to","at","if","in","it","on",/[A-Za-z]+['](d|ve|re|ll|t|s|n)/,/[A-Za-z]+[-][a-z]+/,/[A-Za-z][a-z]{2,}/);return r.contains.push({begin:m(/[ ]+/,"(",i,/[.]?[:]?([.][ ]|[ ])/,"){3}")}),r},O=D("//","$"),N=D("/\\*","\\*/"),R=D("#","$"),j={scope:"number",begin:k,relevance:0},G={scope:"number",begin:_,relevance:0},V={scope:"number",begin:S,relevance:0},$={begin:/(?=\/[^/\n]*\/)/,contains:[{scope:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[P,{begin:/\[/,end:/\]/,relevance:0,contains:[P]}]}]},z={scope:"title",begin:B,relevance:0},q={scope:"title",begin:x,relevance:0},H={begin:"\\.\\s*"+x,relevance:0},W=function(e){return Object.assign(e,{"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{t.data._beginMatch!==e[1]&&t.ignoreMatch()}})};var F=Object.freeze({__proto__:null,MATCH_NOTHING_RE:E,IDENT_RE:B,UNDERSCORE_IDENT_RE:x,NUMBER_RE:k,C_NUMBER_RE:_,BINARY_NUMBER_RE:S,RE_STARTERS_RE:T,SHEBANG:L,BACKSLASH_ESCAPE:P,APOS_STRING_MODE:Q,QUOTE_STRING_MODE:M,PHRASAL_WORDS_MODE:Z,COMMENT:D,C_LINE_COMMENT_MODE:O,C_BLOCK_COMMENT_MODE:N,HASH_COMMENT_MODE:R,NUMBER_MODE:j,C_NUMBER_MODE:G,BINARY_NUMBER_MODE:V,REGEXP_MODE:$,TITLE_MODE:z,UNDERSCORE_TITLE_MODE:q,METHOD_GUARD:H,END_SAME_AS_BEGIN:W});function U(e,t){const n=e.input[e.index-1];"."===n&&t.ignoreMatch()}function Y(e,t){void 0!==e.className&&(e.scope=e.className,delete e.className)}function X(e,t){t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",e.__beforeBegin=U,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,void 0===e.relevance&&(e.relevance=0))}function K(e,t){Array.isArray(e.illegal)&&(e.illegal=b(...e.illegal))}function J(e,t){if(e.match){if(e.begin||e.end)throw new Error("begin & end are not supported with match");e.begin=e.match,delete e.match}}function ee(e,t){void 0===e.relevance&&(e.relevance=1)}const te=(e,t)=>{if(!e.beforeMatch)return;if(e.starts)throw new Error("beforeMatch cannot be used with starts");const n=Object.assign({},e);Object.keys(e).forEach((t=>{delete e[t]})),e.keywords=n.keywords,e.begin=m(n.beforeMatch,h(n.begin)),e.starts={relevance:0,contains:[Object.assign(n,{endsParent:!0})]},e.relevance=0,delete n.beforeMatch},ne=["of","and","for","in","not","or","if","then","parent","list","value"],re="keyword";function ie(e,t,n=re){const r=Object.create(null);return"string"===typeof e?i(n,e.split(" ")):Array.isArray(e)?i(n,e):Object.keys(e).forEach((function(n){Object.assign(r,ie(e[n],t,n))})),r;function i(e,n){t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((function(t){const n=t.split("|");r[n[0]]=[e,se(n[0],n[1])]}))}}function se(e,t){return t?Number(t):ae(e)?0:1}function ae(e){return ne.includes(e.toLowerCase())}const oe={},le=e=>{console.error(e)},ce=(e,...t)=>{console.log(`WARN: ${e}`,...t)},ue=(e,t)=>{oe[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),oe[`${e}/${t}`]=!0)},de=new Error;function Ae(e,t,{key:n}){let r=0;const i=e[n],s={},a={};for(let o=1;o<=t.length;o++)a[o+r]=i[o],s[o+r]=!0,r+=y(t[o-1]);e[n]=a,e[n]._emit=s,e[n]._multi=!0}function pe(e){if(Array.isArray(e.begin)){if(e.skip||e.excludeBegin||e.returnBegin)throw le("skip, excludeBegin, returnBegin not compatible with beginScope: {}"),de;if("object"!==typeof e.beginScope||null===e.beginScope)throw le("beginScope must be object"),de;Ae(e,e.begin,{key:"beginScope"}),e.begin=w(e.begin,{joinWith:""})}}function he(e){if(Array.isArray(e.end)){if(e.skip||e.excludeEnd||e.returnEnd)throw le("skip, excludeEnd, returnEnd not compatible with endScope: {}"),de;if("object"!==typeof e.endScope||null===e.endScope)throw le("endScope must be object"),de;Ae(e,e.end,{key:"endScope"}),e.end=w(e.end,{joinWith:""})}}function ge(e){e.scope&&"object"===typeof e.scope&&null!==e.scope&&(e.beginScope=e.scope,delete e.scope)}function fe(e){ge(e),"string"===typeof e.beginScope&&(e.beginScope={_wrap:e.beginScope}),"string"===typeof e.endScope&&(e.endScope={_wrap:e.endScope}),pe(e),he(e)}function me(e){function t(t,n){return new RegExp(p(t),"m"+(e.case_insensitive?"i":"")+(e.unicodeRegex?"u":"")+(n?"g":""))}class n{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,t){t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),this.matchAt+=y(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map((e=>e[1]));this.matcherRe=t(w(e,{joinWith:"|"}),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const t=this.matcherRe.exec(e);if(!t)return null;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),r=this.matchIndexes[n];return t.splice(0,n),Object.assign(t,r)}}class r{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const t=new n;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))),t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex;let n=t.exec(e);if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}return n&&(this.regexIndex+=n.position+1,this.regexIndex===this.count&&this.considerAll()),n}}function i(e){const t=new r;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin"}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end"}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t}function s(n,r){const a=n;if(n.isCompiled)return a;[Y,J,fe,te].forEach((e=>e(n,r))),e.compilerExtensions.forEach((e=>e(n,r))),n.__beforeBegin=null,[X,K,ee].forEach((e=>e(n,r))),n.isCompiled=!0;let o=null;return"object"===typeof n.keywords&&n.keywords.$pattern&&(n.keywords=Object.assign({},n.keywords),o=n.keywords.$pattern,delete n.keywords.$pattern),o=o||/\w+/,n.keywords&&(n.keywords=ie(n.keywords,e.case_insensitive)),a.keywordPatternRe=t(o,!0),r&&(n.begin||(n.begin=/\B|\b/),a.beginRe=t(a.begin),n.end||n.endsWithParent||(n.end=/\B|\b/),n.end&&(a.endRe=t(a.end)),a.terminatorEnd=p(a.end)||"",n.endsWithParent&&r.terminatorEnd&&(a.terminatorEnd+=(n.end?"|":"")+r.terminatorEnd)),n.illegal&&(a.illegalRe=t(n.illegal)),n.contains||(n.contains=[]),n.contains=[].concat(...n.contains.map((function(e){return be("self"===e?n:e)}))),n.contains.forEach((function(e){s(e,a)})),n.starts&&s(n.starts,r),a.matcher=i(a),a}if(e.compilerExtensions||(e.compilerExtensions=[]),e.contains&&e.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return e.classNameAliases=a(e.classNameAliases||{}),s(e)}function ve(e){return!!e&&(e.endsWithParent||ve(e.starts))}function be(e){return e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((function(t){return a(e,{variants:null},t)}))),e.cachedVariants?e.cachedVariants:ve(e)?a(e,{starts:e.starts?a(e.starts):null}):Object.isFrozen(e)?a(e):e}var ye="11.3.1";class Ce extends Error{constructor(e,t){super(e),this.name="HTMLInjectionError",this.html=t}}const Ie=s,we=a,Ee=Symbol("nomatch"),Be=7,xe=function(e){const t=Object.create(null),n=Object.create(null),s=[];let a=!0;const o="Could not find the language '{}', did you forget to load/include a language module?",l={disableAutodetect:!0,name:"Plain text",contains:[]};let c={ignoreUnescapedHTML:!1,throwUnescapedHTML:!1,noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",cssSelector:"pre code",languages:null,__emitter:A};function u(e){return c.noHighlightRe.test(e)}function d(e){let t=e.className+" ";t+=e.parentNode?e.parentNode.className:"";const n=c.languageDetectRe.exec(t);if(n){const t=M(n[1]);return t||(ce(o.replace("{}",n[1])),ce("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"}return t.split(/\s+/).find((e=>u(e)||M(e)))}function p(e,t,n){let r="",i="";"object"===typeof t?(r=e,n=t.ignoreIllegals,i=t.language):(ue("10.7.0","highlight(lang, code, ...args) has been deprecated."),ue("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),i=e,r=t),void 0===n&&(n=!0);const s={code:r,language:i};R("before:highlight",s);const a=s.result?s.result:v(s.language,s.code,n);return a.code=s.code,R("after:highlight",a),a}function v(e,n,r,s){const l=Object.create(null);function u(e,t){return e.keywords[t]}function d(){if(!S.keywords)return void L.addText(P);let e=0;S.keywordPatternRe.lastIndex=0;let t=S.keywordPatternRe.exec(P),n="";while(t){n+=P.substring(e,t.index);const r=x.case_insensitive?t[0].toLowerCase():t[0],i=u(S,r);if(i){const[e,s]=i;if(L.addText(n),n="",l[r]=(l[r]||0)+1,l[r]<=Be&&(Q+=s),e.startsWith("_"))n+=t[0];else{const n=x.classNameAliases[e]||e;L.addKeyword(t[0],n)}}else n+=t[0];e=S.keywordPatternRe.lastIndex,t=S.keywordPatternRe.exec(P)}n+=P.substr(e),L.addText(n)}function A(){if(""===P)return;let e=null;if("string"===typeof S.subLanguage){if(!t[S.subLanguage])return void L.addText(P);e=v(S.subLanguage,P,!0,T[S.subLanguage]),T[S.subLanguage]=e._top}else e=I(P,S.subLanguage.length?S.subLanguage:null);S.relevance>0&&(Q+=e.relevance),L.addSublanguage(e._emitter,e.language)}function p(){null!=S.subLanguage?A():d(),P=""}function h(e,t){let n=1;while(void 0!==t[n]){if(!e._emit[n]){n++;continue}const r=x.classNameAliases[e[n]]||e[n],i=t[n];r?L.addKeyword(i,r):(P=i,d(),P=""),n++}}function g(e,t){return e.scope&&"string"===typeof e.scope&&L.openNode(x.classNameAliases[e.scope]||e.scope),e.beginScope&&(e.beginScope._wrap?(L.addKeyword(P,x.classNameAliases[e.beginScope._wrap]||e.beginScope._wrap),P=""):e.beginScope._multi&&(h(e.beginScope,t),P="")),S=Object.create(e,{parent:{value:S}}),S}function f(e,t,n){let r=C(e.endRe,n);if(r){if(e["on:end"]){const n=new i(e);e["on:end"](t,n),n.isMatchIgnored&&(r=!1)}if(r){while(e.endsParent&&e.parent)e=e.parent;return e}}if(e.endsWithParent)return f(e.parent,t,n)}function m(e){return 0===S.matcher.regexIndex?(P+=e[0],1):(O=!0,0)}function b(e){const t=e[0],n=e.rule,r=new i(n),s=[n.__beforeBegin,n["on:begin"]];for(const i of s)if(i&&(i(e,r),r.isMatchIgnored))return m(t);return n.skip?P+=t:(n.excludeBegin&&(P+=t),p(),n.returnBegin||n.excludeBegin||(P=t)),g(n,e),n.returnBegin?0:t.length}function y(e){const t=e[0],r=n.substr(e.index),i=f(S,e,r);if(!i)return Ee;const s=S;S.endScope&&S.endScope._wrap?(p(),L.addKeyword(t,S.endScope._wrap)):S.endScope&&S.endScope._multi?(p(),h(S.endScope,e)):s.skip?P+=t:(s.returnEnd||s.excludeEnd||(P+=t),p(),s.excludeEnd&&(P=t));do{S.scope&&L.closeNode(),S.skip||S.subLanguage||(Q+=S.relevance),S=S.parent}while(S!==i.parent);return i.starts&&g(i.starts,e),s.returnEnd?0:t.length}function w(){const e=[];for(let t=S;t!==x;t=t.parent)t.scope&&e.unshift(t.scope);e.forEach((e=>L.openNode(e)))}let E={};function B(t,i){const s=i&&i[0];if(P+=t,null==s)return p(),0;if("begin"===E.type&&"end"===i.type&&E.index===i.index&&""===s){if(P+=n.slice(i.index,i.index+1),!a){const t=new Error(`0 width match regex (${e})`);throw t.languageName=e,t.badRule=E.rule,t}return 1}if(E=i,"begin"===i.type)return b(i);if("illegal"===i.type&&!r){const e=new Error('Illegal lexeme "'+s+'" for mode "'+(S.scope||"")+'"');throw e.mode=S,e}if("end"===i.type){const e=y(i);if(e!==Ee)return e}if("illegal"===i.type&&""===s)return 1;if(D>1e5&&D>3*i.index){const e=new Error("potential infinite loop, way more iterations than matches");throw e}return P+=s,s.length}const x=M(e);if(!x)throw le(o.replace("{}",e)),new Error('Unknown language: "'+e+'"');const k=me(x);let _="",S=s||k;const T={},L=new c.__emitter(c);w();let P="",Q=0,Z=0,D=0,O=!1;try{for(S.matcher.considerAll();;){D++,O?O=!1:S.matcher.considerAll(),S.matcher.lastIndex=Z;const e=S.matcher.exec(n);if(!e)break;const t=n.substring(Z,e.index),r=B(t,e);Z=e.index+r}return B(n.substr(Z)),L.closeAllNodes(),L.finalize(),_=L.toHTML(),{language:e,value:_,relevance:Q,illegal:!1,_emitter:L,_top:S}}catch(N){if(N.message&&N.message.includes("Illegal"))return{language:e,value:Ie(n),illegal:!0,relevance:0,_illegalBy:{message:N.message,index:Z,context:n.slice(Z-100,Z+100),mode:N.mode,resultSoFar:_},_emitter:L};if(a)return{language:e,value:Ie(n),illegal:!1,relevance:0,errorRaised:N,_emitter:L,_top:S};throw N}}function y(e){const t={value:Ie(e),illegal:!1,relevance:0,_top:l,_emitter:new c.__emitter(c)};return t._emitter.addText(e),t}function I(e,n){n=n||c.languages||Object.keys(t);const r=y(e),i=n.filter(M).filter(D).map((t=>v(t,e,!1)));i.unshift(r);const s=i.sort(((e,t)=>{if(e.relevance!==t.relevance)return t.relevance-e.relevance;if(e.language&&t.language){if(M(e.language).supersetOf===t.language)return 1;if(M(t.language).supersetOf===e.language)return-1}return 0})),[a,o]=s,l=a;return l.secondBest=o,l}function w(e,t,r){const i=t&&n[t]||r;e.classList.add("hljs"),e.classList.add(`language-${i}`)}function E(e){let t=null;const n=d(e);if(u(n))return;if(R("before:highlightElement",{el:e,language:n}),e.children.length>0&&(c.ignoreUnescapedHTML||(console.warn("One of your code blocks includes unescaped HTML. This is a potentially serious security risk."),console.warn("https://github.com/highlightjs/highlight.js/issues/2886"),console.warn(e)),c.throwUnescapedHTML)){const t=new Ce("One of your code blocks includes unescaped HTML.",e.innerHTML);throw t}t=e;const r=t.textContent,i=n?p(r,{language:n,ignoreIllegals:!0}):I(r);e.innerHTML=i.value,w(e,n,i.language),e.result={language:i.language,re:i.relevance,relevance:i.relevance},i.secondBest&&(e.secondBest={language:i.secondBest.language,relevance:i.secondBest.relevance}),R("after:highlightElement",{el:e,result:i,text:r})}function B(e){c=we(c,e)}const x=()=>{S(),ue("10.6.0","initHighlighting() deprecated. Use highlightAll() now.")};function k(){S(),ue("10.6.0","initHighlightingOnLoad() deprecated. Use highlightAll() now.")}let _=!1;function S(){if("loading"===document.readyState)return void(_=!0);const e=document.querySelectorAll(c.cssSelector);e.forEach(E)}function T(){_&&S()}function L(n,r){let i=null;try{i=r(e)}catch(s){if(le("Language definition for '{}' could not be registered.".replace("{}",n)),!a)throw s;le(s),i=l}i.name||(i.name=n),t[n]=i,i.rawDefinition=r.bind(null,e),i.aliases&&Z(i.aliases,{languageName:n})}function P(e){delete t[e];for(const t of Object.keys(n))n[t]===e&&delete n[t]}function Q(){return Object.keys(t)}function M(e){return e=(e||"").toLowerCase(),t[e]||t[n[e]]}function Z(e,{languageName:t}){"string"===typeof e&&(e=[e]),e.forEach((e=>{n[e.toLowerCase()]=t}))}function D(e){const t=M(e);return t&&!t.disableAutodetect}function O(e){e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{e["before:highlightBlock"](Object.assign({block:t.el},t))}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{e["after:highlightBlock"](Object.assign({block:t.el},t))})}function N(e){O(e),s.push(e)}function R(e,t){const n=e;s.forEach((function(e){e[n]&&e[n](t)}))}function j(e){return ue("10.7.0","highlightBlock will be removed entirely in v12.0"),ue("10.7.0","Please use highlightElement now."),E(e)}"undefined"!==typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",T,!1),Object.assign(e,{highlight:p,highlightAuto:I,highlightAll:S,highlightElement:E,highlightBlock:j,configure:B,initHighlighting:x,initHighlightingOnLoad:k,registerLanguage:L,unregisterLanguage:P,listLanguages:Q,getLanguage:M,registerAliases:Z,autoDetection:D,inherit:we,addPlugin:N}),e.debugMode=function(){a=!1},e.safeMode=function(){a=!0},e.versionString=ye,e.regex={concat:m,lookahead:h,either:b,optional:f,anyNumberOfTimes:g};for(const i in F)"object"===typeof F[i]&&r(F[i]);return Object.assign(e,F),e};var ke=xe({});e.exports=ke,ke.HighlightJS=ke,ke.default=ke}}]); \ No newline at end of file diff --git a/docs/js/chunk-vendors.bdb7cbba.js b/docs/js/chunk-vendors.bdb7cbba.js new file mode 100644 index 0000000..2b84f94 --- /dev/null +++ b/docs/js/chunk-vendors.bdb7cbba.js @@ -0,0 +1,26 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[998],{1001:function(t,e,n){"use strict";function r(t,e,n,r,o,i,a,s){var c,u="function"===typeof t?t.options:t;if(e&&(u.render=e,u.staticRenderFns=n,u._compiled=!0),r&&(u.functional=!0),i&&(u._scopeId="data-v-"+i),a?(c=function(t){t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,t||"undefined"===typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),o&&o.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(a)},u._ssrRegister=c):o&&(c=s?function(){o.call(this,(u.functional?this.parent:this).$root.$options.shadowRoot)}:o),c)if(u.functional){u._injectStyles=c;var l=u.render;u.render=function(t,e){return c.call(e),l(t,e)}}else{var f=u.beforeCreate;u.beforeCreate=f?[].concat(f,c):[c]}return{exports:t,options:u}}n.d(e,{Z:function(){return r}})},8269:function(t,e,n){(function(e,n){t.exports=n(e)})("undefined"!=typeof n.g?n.g:this,(function(t){if(t.CSS&&t.CSS.escape)return t.CSS.escape;var e=function(t){if(0==arguments.length)throw new TypeError("`CSS.escape` requires an argument.");var e,n=String(t),r=n.length,o=-1,i="",a=n.charCodeAt(0);while(++o=1&&e<=31||127==e||0==o&&e>=48&&e<=57||1==o&&e>=48&&e<=57&&45==a?"\\"+e.toString(16)+" ":(0!=o||1!=r||45!=e)&&(e>=128||45==e||95==e||e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122)?n.charAt(o):"\\"+n.charAt(o):"�";return i};return t.CSS||(t.CSS={}),t.CSS.escape=e,e}))},7152:function(t,e){"use strict"; +/*! + * vue-i18n v8.28.2 + * (c) 2022 kazuya kawaguchi + * Released under the MIT License. + */var n=["compactDisplay","currency","currencyDisplay","currencySign","localeMatcher","notation","numberingSystem","signDisplay","style","unit","unitDisplay","useGrouping","minimumIntegerDigits","minimumFractionDigits","maximumFractionDigits","minimumSignificantDigits","maximumSignificantDigits"],r=["dateStyle","timeStyle","calendar","localeMatcher","hour12","hourCycle","timeZone","formatMatcher","weekday","era","year","month","day","hour","minute","second","timeZoneName"];function o(t,e){"undefined"!==typeof console&&(console.warn("[vue-i18n] "+t),e&&console.warn(e.stack))}function i(t,e){"undefined"!==typeof console&&(console.error("[vue-i18n] "+t),e&&console.error(e.stack))}var a=Array.isArray;function s(t){return null!==t&&"object"===typeof t}function c(t){return"boolean"===typeof t}function u(t){return"string"===typeof t}var l=Object.prototype.toString,f="[object Object]";function p(t){return l.call(t)===f}function h(t){return null===t||void 0===t}function d(t){return"function"===typeof t}function v(){var t=[],e=arguments.length;while(e--)t[e]=arguments[e];var n=null,r=null;return 1===t.length?s(t[0])||a(t[0])?r=t[0]:"string"===typeof t[0]&&(n=t[0]):2===t.length&&("string"===typeof t[0]&&(n=t[0]),(s(t[1])||a(t[1]))&&(r=t[1])),{locale:n,params:r}}function m(t){return JSON.parse(JSON.stringify(t))}function y(t,e){if(t.delete(e))return t}function g(t){var e=[];return t.forEach((function(t){return e.push(t)})),e}function _(t,e){return!!~t.indexOf(e)}var b=Object.prototype.hasOwnProperty;function w(t,e){return b.call(t,e)}function C(t){for(var e=arguments,n=Object(t),r=1;r/g,">").replace(/"/g,""").replace(/'/g,"'")}function x(t){return null!=t&&Object.keys(t).forEach((function(e){"string"==typeof t[e]&&(t[e]=$(t[e]))})),t}function O(t){t.prototype.hasOwnProperty("$i18n")||Object.defineProperty(t.prototype,"$i18n",{get:function(){return this._i18n}}),t.prototype.$t=function(t){var e=[],n=arguments.length-1;while(n-- >0)e[n]=arguments[n+1];var r=this.$i18n;return r._t.apply(r,[t,r.locale,r._getMessages(),this].concat(e))},t.prototype.$tc=function(t,e){var n=[],r=arguments.length-2;while(r-- >0)n[r]=arguments[r+2];var o=this.$i18n;return o._tc.apply(o,[t,o.locale,o._getMessages(),this,e].concat(n))},t.prototype.$te=function(t,e){var n=this.$i18n;return n._te(t,n.locale,n._getMessages(),e)},t.prototype.$d=function(t){var e,n=[],r=arguments.length-1;while(r-- >0)n[r]=arguments[r+1];return(e=this.$i18n).d.apply(e,[t].concat(n))},t.prototype.$n=function(t){var e,n=[],r=arguments.length-1;while(r-- >0)n[r]=arguments[r+1];return(e=this.$i18n).n.apply(e,[t].concat(n))}}function S(t){function e(){this!==this.$root&&this.$options.__INTLIFY_META__&&this.$el&&this.$el.setAttribute("data-intlify",this.$options.__INTLIFY_META__)}return void 0===t&&(t=!1),t?{mounted:e}:{beforeCreate:function(){var t=this.$options;if(t.i18n=t.i18n||(t.__i18nBridge||t.__i18n?{}:null),t.i18n)if(t.i18n instanceof xt){if(t.__i18nBridge||t.__i18n)try{var e=t.i18n&&t.i18n.messages?t.i18n.messages:{},n=t.__i18nBridge||t.__i18n;n.forEach((function(t){e=C(e,JSON.parse(t))})),Object.keys(e).forEach((function(n){t.i18n.mergeLocaleMessage(n,e[n])}))}catch(c){0}this._i18n=t.i18n,this._i18nWatcher=this._i18n.watchI18nData()}else if(p(t.i18n)){var r=this.$root&&this.$root.$i18n&&this.$root.$i18n instanceof xt?this.$root.$i18n:null;if(r&&(t.i18n.root=this.$root,t.i18n.formatter=r.formatter,t.i18n.fallbackLocale=r.fallbackLocale,t.i18n.formatFallbackMessages=r.formatFallbackMessages,t.i18n.silentTranslationWarn=r.silentTranslationWarn,t.i18n.silentFallbackWarn=r.silentFallbackWarn,t.i18n.pluralizationRules=r.pluralizationRules,t.i18n.preserveDirectiveContent=r.preserveDirectiveContent),t.__i18nBridge||t.__i18n)try{var o=t.i18n&&t.i18n.messages?t.i18n.messages:{},i=t.__i18nBridge||t.__i18n;i.forEach((function(t){o=C(o,JSON.parse(t))})),t.i18n.messages=o}catch(c){0}var a=t.i18n,s=a.sharedMessages;s&&p(s)&&(t.i18n.messages=C(t.i18n.messages,s)),this._i18n=new xt(t.i18n),this._i18nWatcher=this._i18n.watchI18nData(),(void 0===t.i18n.sync||t.i18n.sync)&&(this._localeWatcher=this.$i18n.watchLocale()),r&&r.onComponentInstanceCreated(this._i18n)}else 0;else this.$root&&this.$root.$i18n&&this.$root.$i18n instanceof xt?this._i18n=this.$root.$i18n:t.parent&&t.parent.$i18n&&t.parent.$i18n instanceof xt&&(this._i18n=t.parent.$i18n)},beforeMount:function(){var t=this.$options;t.i18n=t.i18n||(t.__i18nBridge||t.__i18n?{}:null),t.i18n?(t.i18n instanceof xt||p(t.i18n))&&(this._i18n.subscribeDataChanging(this),this._subscribing=!0):(this.$root&&this.$root.$i18n&&this.$root.$i18n instanceof xt||t.parent&&t.parent.$i18n&&t.parent.$i18n instanceof xt)&&(this._i18n.subscribeDataChanging(this),this._subscribing=!0)},mounted:e,beforeDestroy:function(){if(this._i18n){var t=this;this.$nextTick((function(){t._subscribing&&(t._i18n.unsubscribeDataChanging(t),delete t._subscribing),t._i18nWatcher&&(t._i18nWatcher(),t._i18n.destroyVM(),delete t._i18nWatcher),t._localeWatcher&&(t._localeWatcher(),delete t._localeWatcher)}))}}}}var T={name:"i18n",functional:!0,props:{tag:{type:[String,Boolean,Object],default:"span"},path:{type:String,required:!0},locale:{type:String},places:{type:[Array,Object]}},render:function(t,e){var n=e.data,r=e.parent,o=e.props,i=e.slots,a=r.$i18n;if(a){var s=o.path,c=o.locale,u=o.places,l=i(),f=a.i(s,c,E(l)||u?j(l.default,u):l),p=o.tag&&!0!==o.tag||!1===o.tag?o.tag:"span";return p?t(p,n,f):f}}};function E(t){var e;for(e in t)if("default"!==e)return!1;return Boolean(e)}function j(t,e){var n=e?A(e):{};if(!t)return n;t=t.filter((function(t){return t.tag||""!==t.text.trim()}));var r=t.every(R);return t.reduce(r?F:I,n)}function A(t){return Array.isArray(t)?t.reduce(I,{}):Object.assign({},t)}function F(t,e){return e.data&&e.data.attrs&&e.data.attrs.place&&(t[e.data.attrs.place]=e),t}function I(t,e,n){return t[n]=e,t}function R(t){return Boolean(t.data&&t.data.attrs&&t.data.attrs.place)}var M,L={name:"i18n-n",functional:!0,props:{tag:{type:[String,Boolean,Object],default:"span"},value:{type:Number,required:!0},format:{type:[String,Object]},locale:{type:String}},render:function(t,e){var r=e.props,o=e.parent,i=e.data,a=o.$i18n;if(!a)return null;var c=null,l=null;u(r.format)?c=r.format:s(r.format)&&(r.format.key&&(c=r.format.key),l=Object.keys(r.format).reduce((function(t,e){var o;return _(n,e)?Object.assign({},t,(o={},o[e]=r.format[e],o)):t}),null));var f=r.locale||a.locale,p=a._ntp(r.value,f,c,l),h=p.map((function(t,e){var n,r=i.scopedSlots&&i.scopedSlots[t.type];return r?r((n={},n[t.type]=t.value,n.index=e,n.parts=p,n)):t.value})),d=r.tag&&!0!==r.tag||!1===r.tag?r.tag:"span";return d?t(d,{attrs:i.attrs,class:i["class"],staticClass:i.staticClass},h):h}};function P(t,e,n){V(t,n)&&B(t,e,n)}function D(t,e,n,r){if(V(t,n)){var o=n.context.$i18n;U(t,n)&&k(e.value,e.oldValue)&&k(t._localeMessage,o.getLocaleMessage(o.locale))||B(t,e,n)}}function N(t,e,n,r){var i=n.context;if(i){var a=n.context.$i18n||{};e.modifiers.preserve||a.preserveDirectiveContent||(t.textContent=""),t._vt=void 0,delete t["_vt"],t._locale=void 0,delete t["_locale"],t._localeMessage=void 0,delete t["_localeMessage"]}else o("Vue instance does not exists in VNode context")}function V(t,e){var n=e.context;return n?!!n.$i18n||(o("VueI18n instance does not exists in Vue instance"),!1):(o("Vue instance does not exists in VNode context"),!1)}function U(t,e){var n=e.context;return t._locale===n.$i18n.locale}function B(t,e,n){var r,i,a=e.value,s=W(a),c=s.path,u=s.locale,l=s.args,f=s.choice;if(c||u||l)if(c){var p=n.context;t._vt=t.textContent=null!=f?(r=p.$i18n).tc.apply(r,[c,f].concat(H(u,l))):(i=p.$i18n).t.apply(i,[c].concat(H(u,l))),t._locale=p.$i18n.locale,t._localeMessage=p.$i18n.getLocaleMessage(p.$i18n.locale)}else o("`path` is required in v-t directive");else o("value type not supported")}function W(t){var e,n,r,o;return u(t)?e=t:p(t)&&(e=t.path,n=t.locale,r=t.args,o=t.choice),{path:e,locale:n,args:r,choice:o}}function H(t,e){var n=[];return t&&n.push(t),e&&(Array.isArray(e)||p(e))&&n.push(e),n}function z(t,e){void 0===e&&(e={bridge:!1}),z.installed=!0,M=t;M.version&&Number(M.version.split(".")[0]);O(M),M.mixin(S(e.bridge)),M.directive("t",{bind:P,update:D,unbind:N}),M.component(T.name,T),M.component(L.name,L);var n=M.config.optionMergeStrategies;n.i18n=function(t,e){return void 0===e?t:e}}var q=function(){this._caches=Object.create(null)};q.prototype.interpolate=function(t,e){if(!e)return[t];var n=this._caches[t];return n||(n=G(t),this._caches[t]=n),J(n,e)};var K=/^(?:\d)+/,Z=/^(?:\w)+/;function G(t){var e=[],n=0,r="";while(n0)f--,l=it,p[X]();else{if(f=0,void 0===n)return!1;if(n=vt(n),!1===n)return!1;p[Y]()}};while(null!==l)if(u++,e=t[u],"\\"!==e||!h()){if(o=dt(e),s=lt[l],i=s[o]||s["else"]||ut,i===ut)return;if(l=i[0],a=p[i[1]],a&&(r=i[2],r=void 0===r?e:r,!1===a()))return;if(l===ct)return c}}var yt=function(){this._cache=Object.create(null)};yt.prototype.parsePath=function(t){var e=this._cache[t];return e||(e=mt(t),e&&(this._cache[t]=e)),e||[]},yt.prototype.getPathValue=function(t,e){if(!s(t))return null;var n=this.parsePath(e);if(0===n.length)return null;var r=n.length,o=t,i=0;while(i/,bt=/(?:@(?:\.[a-zA-Z]+)?:(?:[\w\-_|./]+|\([\w\-_:|./]+\)))/g,wt=/^@(?:\.([a-zA-Z]+))?:/,Ct=/[()]/g,kt={upper:function(t){return t.toLocaleUpperCase()},lower:function(t){return t.toLocaleLowerCase()},capitalize:function(t){return""+t.charAt(0).toLocaleUpperCase()+t.substr(1)}},$t=new q,xt=function(t){var e=this;void 0===t&&(t={}),!M&&"undefined"!==typeof window&&window.Vue&&z(window.Vue);var n=t.locale||"en-US",r=!1!==t.fallbackLocale&&(t.fallbackLocale||"en-US"),o=t.messages||{},i=t.dateTimeFormats||t.datetimeFormats||{},a=t.numberFormats||{};this._vm=null,this._formatter=t.formatter||$t,this._modifiers=t.modifiers||{},this._missing=t.missing||null,this._root=t.root||null,this._sync=void 0===t.sync||!!t.sync,this._fallbackRoot=void 0===t.fallbackRoot||!!t.fallbackRoot,this._fallbackRootWithEmptyString=void 0===t.fallbackRootWithEmptyString||!!t.fallbackRootWithEmptyString,this._formatFallbackMessages=void 0!==t.formatFallbackMessages&&!!t.formatFallbackMessages,this._silentTranslationWarn=void 0!==t.silentTranslationWarn&&t.silentTranslationWarn,this._silentFallbackWarn=void 0!==t.silentFallbackWarn&&!!t.silentFallbackWarn,this._dateTimeFormatters={},this._numberFormatters={},this._path=new yt,this._dataListeners=new Set,this._componentInstanceCreatedListener=t.componentInstanceCreatedListener||null,this._preserveDirectiveContent=void 0!==t.preserveDirectiveContent&&!!t.preserveDirectiveContent,this.pluralizationRules=t.pluralizationRules||{},this._warnHtmlInMessage=t.warnHtmlInMessage||"off",this._postTranslation=t.postTranslation||null,this._escapeParameterHtml=t.escapeParameterHtml||!1,"__VUE_I18N_BRIDGE__"in t&&(this.__VUE_I18N_BRIDGE__=t.__VUE_I18N_BRIDGE__),this.getChoiceIndex=function(t,n){var r=Object.getPrototypeOf(e);if(r&&r.getChoiceIndex){var o=r.getChoiceIndex;return o.call(e,t,n)}var i=function(t,e){return t=Math.abs(t),2===e?t?t>1?1:0:1:t?Math.min(t,2):0};return e.locale in e.pluralizationRules?e.pluralizationRules[e.locale].apply(e,[t,n]):i(t,n)},this._exist=function(t,n){return!(!t||!n)&&(!h(e._path.getPathValue(t,n))||!!t[n])},"warn"!==this._warnHtmlInMessage&&"error"!==this._warnHtmlInMessage||Object.keys(o).forEach((function(t){e._checkLocaleMessage(t,e._warnHtmlInMessage,o[t])})),this._initVM({locale:n,fallbackLocale:r,messages:o,dateTimeFormats:i,numberFormats:a})},Ot={vm:{configurable:!0},messages:{configurable:!0},dateTimeFormats:{configurable:!0},numberFormats:{configurable:!0},availableLocales:{configurable:!0},locale:{configurable:!0},fallbackLocale:{configurable:!0},formatFallbackMessages:{configurable:!0},missing:{configurable:!0},formatter:{configurable:!0},silentTranslationWarn:{configurable:!0},silentFallbackWarn:{configurable:!0},preserveDirectiveContent:{configurable:!0},warnHtmlInMessage:{configurable:!0},postTranslation:{configurable:!0},sync:{configurable:!0}};xt.prototype._checkLocaleMessage=function(t,e,n){var r=[],s=function(t,e,n,r){if(p(n))Object.keys(n).forEach((function(o){var i=n[o];p(i)?(r.push(o),r.push("."),s(t,e,i,r),r.pop(),r.pop()):(r.push(o),s(t,e,i,r),r.pop())}));else if(a(n))n.forEach((function(n,o){p(n)?(r.push("["+o+"]"),r.push("."),s(t,e,n,r),r.pop(),r.pop()):(r.push("["+o+"]"),s(t,e,n,r),r.pop())}));else if(u(n)){var c=_t.test(n);if(c){var l="Detected HTML in message '"+n+"' of keypath '"+r.join("")+"' at '"+e+"'. Consider component interpolation with '' to avoid XSS. See https://bit.ly/2ZqJzkp";"warn"===t?o(l):"error"===t&&i(l)}}};s(e,t,n,r)},xt.prototype._initVM=function(t){var e=M.config.silent;M.config.silent=!0,this._vm=new M({data:t,__VUE18N__INSTANCE__:!0}),M.config.silent=e},xt.prototype.destroyVM=function(){this._vm.$destroy()},xt.prototype.subscribeDataChanging=function(t){this._dataListeners.add(t)},xt.prototype.unsubscribeDataChanging=function(t){y(this._dataListeners,t)},xt.prototype.watchI18nData=function(){var t=this;return this._vm.$watch("$data",(function(){var e=g(t._dataListeners),n=e.length;while(n--)M.nextTick((function(){e[n]&&e[n].$forceUpdate()}))}),{deep:!0})},xt.prototype.watchLocale=function(t){if(t){if(!this.__VUE_I18N_BRIDGE__)return null;var e=this,n=this._vm;return this.vm.$watch("locale",(function(r){n.$set(n,"locale",r),e.__VUE_I18N_BRIDGE__&&t&&(t.locale.value=r),n.$forceUpdate()}),{immediate:!0})}if(!this._sync||!this._root)return null;var r=this._vm;return this._root.$i18n.vm.$watch("locale",(function(t){r.$set(r,"locale",t),r.$forceUpdate()}),{immediate:!0})},xt.prototype.onComponentInstanceCreated=function(t){this._componentInstanceCreatedListener&&this._componentInstanceCreatedListener(t,this)},Ot.vm.get=function(){return this._vm},Ot.messages.get=function(){return m(this._getMessages())},Ot.dateTimeFormats.get=function(){return m(this._getDateTimeFormats())},Ot.numberFormats.get=function(){return m(this._getNumberFormats())},Ot.availableLocales.get=function(){return Object.keys(this.messages).sort()},Ot.locale.get=function(){return this._vm.locale},Ot.locale.set=function(t){this._vm.$set(this._vm,"locale",t)},Ot.fallbackLocale.get=function(){return this._vm.fallbackLocale},Ot.fallbackLocale.set=function(t){this._localeChainCache={},this._vm.$set(this._vm,"fallbackLocale",t)},Ot.formatFallbackMessages.get=function(){return this._formatFallbackMessages},Ot.formatFallbackMessages.set=function(t){this._formatFallbackMessages=t},Ot.missing.get=function(){return this._missing},Ot.missing.set=function(t){this._missing=t},Ot.formatter.get=function(){return this._formatter},Ot.formatter.set=function(t){this._formatter=t},Ot.silentTranslationWarn.get=function(){return this._silentTranslationWarn},Ot.silentTranslationWarn.set=function(t){this._silentTranslationWarn=t},Ot.silentFallbackWarn.get=function(){return this._silentFallbackWarn},Ot.silentFallbackWarn.set=function(t){this._silentFallbackWarn=t},Ot.preserveDirectiveContent.get=function(){return this._preserveDirectiveContent},Ot.preserveDirectiveContent.set=function(t){this._preserveDirectiveContent=t},Ot.warnHtmlInMessage.get=function(){return this._warnHtmlInMessage},Ot.warnHtmlInMessage.set=function(t){var e=this,n=this._warnHtmlInMessage;if(this._warnHtmlInMessage=t,n!==t&&("warn"===t||"error"===t)){var r=this._getMessages();Object.keys(r).forEach((function(t){e._checkLocaleMessage(t,e._warnHtmlInMessage,r[t])}))}},Ot.postTranslation.get=function(){return this._postTranslation},Ot.postTranslation.set=function(t){this._postTranslation=t},Ot.sync.get=function(){return this._sync},Ot.sync.set=function(t){this._sync=t},xt.prototype._getMessages=function(){return this._vm.messages},xt.prototype._getDateTimeFormats=function(){return this._vm.dateTimeFormats},xt.prototype._getNumberFormats=function(){return this._vm.numberFormats},xt.prototype._warnDefault=function(t,e,n,r,o,i){if(!h(n))return n;if(this._missing){var a=this._missing.apply(null,[t,e,r,o]);if(u(a))return a}else 0;if(this._formatFallbackMessages){var s=v.apply(void 0,o);return this._render(e,i,s.params,e)}return e},xt.prototype._isFallbackRoot=function(t){return(this._fallbackRootWithEmptyString?!t:h(t))&&!h(this._root)&&this._fallbackRoot},xt.prototype._isSilentFallbackWarn=function(t){return this._silentFallbackWarn instanceof RegExp?this._silentFallbackWarn.test(t):this._silentFallbackWarn},xt.prototype._isSilentFallback=function(t,e){return this._isSilentFallbackWarn(e)&&(this._isFallbackRoot()||t!==this.fallbackLocale)},xt.prototype._isSilentTranslationWarn=function(t){return this._silentTranslationWarn instanceof RegExp?this._silentTranslationWarn.test(t):this._silentTranslationWarn},xt.prototype._interpolate=function(t,e,n,r,o,i,s){if(!e)return null;var c,l=this._path.getPathValue(e,n);if(a(l)||p(l))return l;if(h(l)){if(!p(e))return null;if(c=e[n],!u(c)&&!d(c))return null}else{if(!u(l)&&!d(l))return null;c=l}return u(c)&&(c.indexOf("@:")>=0||c.indexOf("@.")>=0)&&(c=this._link(t,e,c,r,"raw",i,s)),this._render(c,o,i,n)},xt.prototype._link=function(t,e,n,r,o,i,s){var c=n,u=c.match(bt);for(var l in u)if(u.hasOwnProperty(l)){var f=u[l],p=f.match(wt),h=p[0],d=p[1],v=f.replace(h,"").replace(Ct,"");if(_(s,v))return c;s.push(v);var m=this._interpolate(t,e,v,r,"raw"===o?"string":o,"raw"===o?void 0:i,s);if(this._isFallbackRoot(m)){if(!this._root)throw Error("unexpected error");var y=this._root.$i18n;m=y._translate(y._getMessages(),y.locale,y.fallbackLocale,v,r,o,i)}m=this._warnDefault(t,v,m,r,a(i)?i:[i],o),this._modifiers.hasOwnProperty(d)?m=this._modifiers[d](m):kt.hasOwnProperty(d)&&(m=kt[d](m)),s.pop(),c=m?c.replace(f,m):c}return c},xt.prototype._createMessageContext=function(t,e,n,r){var o=this,i=a(t)?t:[],c=s(t)?t:{},u=function(t){return i[t]},l=function(t){return c[t]},f=this._getMessages(),p=this.locale;return{list:u,named:l,values:t,formatter:e,path:n,messages:f,locale:p,linked:function(t){return o._interpolate(p,f[p]||{},t,null,r,void 0,[t])}}},xt.prototype._render=function(t,e,n,r){if(d(t))return t(this._createMessageContext(n,this._formatter||$t,r,e));var o=this._formatter.interpolate(t,n,r);return o||(o=$t.interpolate(t,n,r)),"string"!==e||u(o)?o:o.join("")},xt.prototype._appendItemToChain=function(t,e,n){var r=!1;return _(t,e)||(r=!0,e&&(r="!"!==e[e.length-1],e=e.replace(/!/g,""),t.push(e),n&&n[e]&&(r=n[e]))),r},xt.prototype._appendLocaleToChain=function(t,e,n){var r,o=e.split("-");do{var i=o.join("-");r=this._appendItemToChain(t,i,n),o.splice(-1,1)}while(o.length&&!0===r);return r},xt.prototype._appendBlockToChain=function(t,e,n){for(var r=!0,o=0;o0)i[a]=arguments[a+4];if(!t)return"";var s=v.apply(void 0,i);this._escapeParameterHtml&&(s.params=x(s.params));var c=s.locale||e,u=this._translate(n,c,this.fallbackLocale,t,r,"string",s.params);if(this._isFallbackRoot(u)){if(!this._root)throw Error("unexpected error");return(o=this._root).$t.apply(o,[t].concat(i))}return u=this._warnDefault(c,t,u,r,i,"string"),this._postTranslation&&null!==u&&void 0!==u&&(u=this._postTranslation(u,t)),u},xt.prototype.t=function(t){var e,n=[],r=arguments.length-1;while(r-- >0)n[r]=arguments[r+1];return(e=this)._t.apply(e,[t,this.locale,this._getMessages(),null].concat(n))},xt.prototype._i=function(t,e,n,r,o){var i=this._translate(n,e,this.fallbackLocale,t,r,"raw",o);if(this._isFallbackRoot(i)){if(!this._root)throw Error("unexpected error");return this._root.$i18n.i(t,e,o)}return this._warnDefault(e,t,i,r,[o],"raw")},xt.prototype.i=function(t,e,n){return t?(u(e)||(e=this.locale),this._i(t,e,this._getMessages(),null,n)):""},xt.prototype._tc=function(t,e,n,r,o){var i,a=[],s=arguments.length-5;while(s-- >0)a[s]=arguments[s+5];if(!t)return"";void 0===o&&(o=1);var c={count:o,n:o},u=v.apply(void 0,a);return u.params=Object.assign(c,u.params),a=null===u.locale?[u.params]:[u.locale,u.params],this.fetchChoice((i=this)._t.apply(i,[t,e,n,r].concat(a)),o)},xt.prototype.fetchChoice=function(t,e){if(!t||!u(t))return null;var n=t.split("|");return e=this.getChoiceIndex(e,n.length),n[e]?n[e].trim():t},xt.prototype.tc=function(t,e){var n,r=[],o=arguments.length-2;while(o-- >0)r[o]=arguments[o+2];return(n=this)._tc.apply(n,[t,this.locale,this._getMessages(),null,e].concat(r))},xt.prototype._te=function(t,e,n){var r=[],o=arguments.length-3;while(o-- >0)r[o]=arguments[o+3];var i=v.apply(void 0,r).locale||e;return this._exist(n[i],t)},xt.prototype.te=function(t,e){return this._te(t,this.locale,this._getMessages(),e)},xt.prototype.getLocaleMessage=function(t){return m(this._vm.messages[t]||{})},xt.prototype.setLocaleMessage=function(t,e){"warn"!==this._warnHtmlInMessage&&"error"!==this._warnHtmlInMessage||this._checkLocaleMessage(t,this._warnHtmlInMessage,e),this._vm.$set(this._vm.messages,t,e)},xt.prototype.mergeLocaleMessage=function(t,e){"warn"!==this._warnHtmlInMessage&&"error"!==this._warnHtmlInMessage||this._checkLocaleMessage(t,this._warnHtmlInMessage,e),this._vm.$set(this._vm.messages,t,C("undefined"!==typeof this._vm.messages[t]&&Object.keys(this._vm.messages[t]).length?Object.assign({},this._vm.messages[t]):{},e))},xt.prototype.getDateTimeFormat=function(t){return m(this._vm.dateTimeFormats[t]||{})},xt.prototype.setDateTimeFormat=function(t,e){this._vm.$set(this._vm.dateTimeFormats,t,e),this._clearDateTimeFormat(t,e)},xt.prototype.mergeDateTimeFormat=function(t,e){this._vm.$set(this._vm.dateTimeFormats,t,C(this._vm.dateTimeFormats[t]||{},e)),this._clearDateTimeFormat(t,e)},xt.prototype._clearDateTimeFormat=function(t,e){for(var n in e){var r=t+"__"+n;this._dateTimeFormatters.hasOwnProperty(r)&&delete this._dateTimeFormatters[r]}},xt.prototype._localizeDateTime=function(t,e,n,r,o,i){for(var a=e,s=r[a],c=this._getLocaleChain(e,n),u=0;u0)e[n]=arguments[n+1];var o=this.locale,i=null,a=null;return 1===e.length?(u(e[0])?i=e[0]:s(e[0])&&(e[0].locale&&(o=e[0].locale),e[0].key&&(i=e[0].key)),a=Object.keys(e[0]).reduce((function(t,n){var o;return _(r,n)?Object.assign({},t,(o={},o[n]=e[0][n],o)):t}),null)):2===e.length&&(u(e[0])&&(i=e[0]),u(e[1])&&(o=e[1])),this._d(t,o,i,a)},xt.prototype.getNumberFormat=function(t){return m(this._vm.numberFormats[t]||{})},xt.prototype.setNumberFormat=function(t,e){this._vm.$set(this._vm.numberFormats,t,e),this._clearNumberFormat(t,e)},xt.prototype.mergeNumberFormat=function(t,e){this._vm.$set(this._vm.numberFormats,t,C(this._vm.numberFormats[t]||{},e)),this._clearNumberFormat(t,e)},xt.prototype._clearNumberFormat=function(t,e){for(var n in e){var r=t+"__"+n;this._numberFormatters.hasOwnProperty(r)&&delete this._numberFormatters[r]}},xt.prototype._getNumberFormatter=function(t,e,n,r,o,i){for(var a=e,s=r[a],c=this._getLocaleChain(e,n),u=0;u0)e[r]=arguments[r+1];var o=this.locale,i=null,a=null;return 1===e.length?u(e[0])?i=e[0]:s(e[0])&&(e[0].locale&&(o=e[0].locale),e[0].key&&(i=e[0].key),a=Object.keys(e[0]).reduce((function(t,r){var o;return _(n,r)?Object.assign({},t,(o={},o[r]=e[0][r],o)):t}),null)):2===e.length&&(u(e[0])&&(i=e[0]),u(e[1])&&(o=e[1])),this._n(t,o,i,a)},xt.prototype._ntp=function(t,e,n,r){if(!xt.availabilities.numberFormat)return[];if(!n){var o=r?new Intl.NumberFormat(e,r):new Intl.NumberFormat(e);return o.formatToParts(t)}var i=this._getNumberFormatter(t,e,this.fallbackLocale,this._getNumberFormats(),n,r),a=i&&i.formatToParts(t);if(this._isFallbackRoot(a)){if(!this._root)throw Error("unexpected error");return this._root.$i18n._ntp(t,e,n,r)}return a||[]},Object.defineProperties(xt.prototype,Ot),Object.defineProperty(xt,"availabilities",{get:function(){if(!gt){var t="undefined"!==typeof Intl;gt={dateTimeFormat:t&&"undefined"!==typeof Intl.DateTimeFormat,numberFormat:t&&"undefined"!==typeof Intl.NumberFormat}}return gt}}),xt.install=z,xt.version="8.28.2",e["Z"]=xt},8345:function(t,e){"use strict"; +/*! + * vue-router v3.5.2 + * (c) 2021 Evan You + * @license MIT + */function n(t,e){0}function r(t,e){for(var n in e)t[n]=e[n];return t}var o=/[!'()*]/g,i=function(t){return"%"+t.charCodeAt(0).toString(16)},a=/%2C/g,s=function(t){return encodeURIComponent(t).replace(o,i).replace(a,",")};function c(t){try{return decodeURIComponent(t)}catch(e){0}return t}function u(t,e,n){void 0===e&&(e={});var r,o=n||f;try{r=o(t||"")}catch(s){r={}}for(var i in e){var a=e[i];r[i]=Array.isArray(a)?a.map(l):l(a)}return r}var l=function(t){return null==t||"object"===typeof t?t:String(t)};function f(t){var e={};return t=t.trim().replace(/^(\?|#|&)/,""),t?(t.split("&").forEach((function(t){var n=t.replace(/\+/g," ").split("="),r=c(n.shift()),o=n.length>0?c(n.join("=")):null;void 0===e[r]?e[r]=o:Array.isArray(e[r])?e[r].push(o):e[r]=[e[r],o]})),e):e}function p(t){var e=t?Object.keys(t).map((function(e){var n=t[e];if(void 0===n)return"";if(null===n)return s(e);if(Array.isArray(n)){var r=[];return n.forEach((function(t){void 0!==t&&(null===t?r.push(s(e)):r.push(s(e)+"="+s(t)))})),r.join("&")}return s(e)+"="+s(n)})).filter((function(t){return t.length>0})).join("&"):null;return e?"?"+e:""}var h=/\/?$/;function d(t,e,n,r){var o=r&&r.options.stringifyQuery,i=e.query||{};try{i=v(i)}catch(s){}var a={name:e.name||t&&t.name,meta:t&&t.meta||{},path:e.path||"/",hash:e.hash||"",query:i,params:e.params||{},fullPath:g(e,o),matched:t?y(t):[]};return n&&(a.redirectedFrom=g(n,o)),Object.freeze(a)}function v(t){if(Array.isArray(t))return t.map(v);if(t&&"object"===typeof t){var e={};for(var n in t)e[n]=v(t[n]);return e}return t}var m=d(null,{path:"/"});function y(t){var e=[];while(t)e.unshift(t),t=t.parent;return e}function g(t,e){var n=t.path,r=t.query;void 0===r&&(r={});var o=t.hash;void 0===o&&(o="");var i=e||p;return(n||"/")+i(r)+o}function _(t,e,n){return e===m?t===e:!!e&&(t.path&&e.path?t.path.replace(h,"")===e.path.replace(h,"")&&(n||t.hash===e.hash&&b(t.query,e.query)):!(!t.name||!e.name)&&(t.name===e.name&&(n||t.hash===e.hash&&b(t.query,e.query)&&b(t.params,e.params))))}function b(t,e){if(void 0===t&&(t={}),void 0===e&&(e={}),!t||!e)return t===e;var n=Object.keys(t).sort(),r=Object.keys(e).sort();return n.length===r.length&&n.every((function(n,o){var i=t[n],a=r[o];if(a!==n)return!1;var s=e[n];return null==i||null==s?i===s:"object"===typeof i&&"object"===typeof s?b(i,s):String(i)===String(s)}))}function w(t,e){return 0===t.path.replace(h,"/").indexOf(e.path.replace(h,"/"))&&(!e.hash||t.hash===e.hash)&&C(t.query,e.query)}function C(t,e){for(var n in e)if(!(n in t))return!1;return!0}function k(t){for(var e=0;e=0&&(e=t.slice(r),t=t.slice(0,r));var o=t.indexOf("?");return o>=0&&(n=t.slice(o+1),t=t.slice(0,o)),{path:t,query:n,hash:e}}function E(t){return t.replace(/\/\//g,"/")}var j=Array.isArray||function(t){return"[object Array]"==Object.prototype.toString.call(t)},A=J,F=P,I=D,R=U,M=G,L=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))"].join("|"),"g");function P(t,e){var n,r=[],o=0,i=0,a="",s=e&&e.delimiter||"/";while(null!=(n=L.exec(t))){var c=n[0],u=n[1],l=n.index;if(a+=t.slice(i,l),i=l+c.length,u)a+=u[1];else{var f=t[i],p=n[2],h=n[3],d=n[4],v=n[5],m=n[6],y=n[7];a&&(r.push(a),a="");var g=null!=p&&null!=f&&f!==p,_="+"===m||"*"===m,b="?"===m||"*"===m,w=n[2]||s,C=d||v;r.push({name:h||o++,prefix:p||"",delimiter:w,optional:b,repeat:_,partial:g,asterisk:!!y,pattern:C?W(C):y?".*":"[^"+B(w)+"]+?"})}}return i1||!$.length)return 0===$.length?t():t("span",{},$)}if("a"===this.tag)k.on=C,k.attrs={href:c,"aria-current":g};else{var x=at(this.$slots.default);if(x){x.isStatic=!1;var O=x.data=r({},x.data);for(var S in O.on=O.on||{},O.on){var T=O.on[S];S in C&&(O.on[S]=Array.isArray(T)?T:[T])}for(var E in C)E in O.on?O.on[E].push(C[E]):O.on[E]=b;var j=x.data.attrs=r({},x.data.attrs);j.href=c,j["aria-current"]=g}else k.on=C}return t(this.tag,k,this.$slots.default)}};function it(t){if(!(t.metaKey||t.altKey||t.ctrlKey||t.shiftKey)&&!t.defaultPrevented&&(void 0===t.button||0===t.button)){if(t.currentTarget&&t.currentTarget.getAttribute){var e=t.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(e))return}return t.preventDefault&&t.preventDefault(),!0}}function at(t){if(t)for(var e,n=0;n-1&&(s.params[f]=n.params[f]);return s.path=Y(u.path,s.params,'named route "'+c+'"'),p(u,s,a)}if(s.path){s.params={};for(var h=0;h=t.length?n():t[o]?e(t[o],(function(){r(o+1)})):r(o+1)};r(0)}var Dt={redirected:2,aborted:4,cancelled:8,duplicated:16};function Nt(t,e){return Wt(t,e,Dt.redirected,'Redirected when going from "'+t.fullPath+'" to "'+zt(e)+'" via a navigation guard.')}function Vt(t,e){var n=Wt(t,e,Dt.duplicated,'Avoided redundant navigation to current location: "'+t.fullPath+'".');return n.name="NavigationDuplicated",n}function Ut(t,e){return Wt(t,e,Dt.cancelled,'Navigation cancelled from "'+t.fullPath+'" to "'+e.fullPath+'" with a new navigation.')}function Bt(t,e){return Wt(t,e,Dt.aborted,'Navigation aborted from "'+t.fullPath+'" to "'+e.fullPath+'" via a navigation guard.')}function Wt(t,e,n,r){var o=new Error(r);return o._isRouter=!0,o.from=t,o.to=e,o.type=n,o}var Ht=["params","query","hash"];function zt(t){if("string"===typeof t)return t;if("path"in t)return t.path;var e={};return Ht.forEach((function(n){n in t&&(e[n]=t[n])})),JSON.stringify(e,null,2)}function qt(t){return Object.prototype.toString.call(t).indexOf("Error")>-1}function Kt(t,e){return qt(t)&&t._isRouter&&(null==e||t.type===e)}function Zt(t){return function(e,n,r){var o=!1,i=0,a=null;Gt(t,(function(t,e,n,s){if("function"===typeof t&&void 0===t.cid){o=!0,i++;var c,u=Qt((function(e){Yt(e)&&(e=e.default),t.resolved="function"===typeof e?e:tt.extend(e),n.components[s]=e,i--,i<=0&&r()})),l=Qt((function(t){var e="Failed to resolve async component "+s+": "+t;a||(a=qt(t)?t:new Error(e),r(a))}));try{c=t(u,l)}catch(p){l(p)}if(c)if("function"===typeof c.then)c.then(u,l);else{var f=c.component;f&&"function"===typeof f.then&&f.then(u,l)}}})),o||r()}}function Gt(t,e){return Jt(t.map((function(t){return Object.keys(t.components).map((function(n){return e(t.components[n],t.instances[n],t,n)}))})))}function Jt(t){return Array.prototype.concat.apply([],t)}var Xt="function"===typeof Symbol&&"symbol"===typeof Symbol.toStringTag;function Yt(t){return t.__esModule||Xt&&"Module"===t[Symbol.toStringTag]}function Qt(t){var e=!1;return function(){var n=[],r=arguments.length;while(r--)n[r]=arguments[r];if(!e)return e=!0,t.apply(this,n)}}var te=function(t,e){this.router=t,this.base=ee(e),this.current=m,this.pending=null,this.ready=!1,this.readyCbs=[],this.readyErrorCbs=[],this.errorCbs=[],this.listeners=[]};function ee(t){if(!t)if(ct){var e=document.querySelector("base");t=e&&e.getAttribute("href")||"/",t=t.replace(/^https?:\/\/[^\/]+/,"")}else t="/";return"/"!==t.charAt(0)&&(t="/"+t),t.replace(/\/$/,"")}function ne(t,e){var n,r=Math.max(t.length,e.length);for(n=0;n0)){var e=this.router,n=e.options.scrollBehavior,r=Rt&&n;r&&this.listeners.push(Ct());var o=function(){var n=t.current,o=fe(t.base);t.current===m&&o===t._startLocation||t.transitionTo(o,(function(t){r&&kt(e,t,n,!0)}))};window.addEventListener("popstate",o),this.listeners.push((function(){window.removeEventListener("popstate",o)}))}},e.prototype.go=function(t){window.history.go(t)},e.prototype.push=function(t,e,n){var r=this,o=this,i=o.current;this.transitionTo(t,(function(t){Mt(E(r.base+t.fullPath)),kt(r.router,t,i,!1),e&&e(t)}),n)},e.prototype.replace=function(t,e,n){var r=this,o=this,i=o.current;this.transitionTo(t,(function(t){Lt(E(r.base+t.fullPath)),kt(r.router,t,i,!1),e&&e(t)}),n)},e.prototype.ensureURL=function(t){if(fe(this.base)!==this.current.fullPath){var e=E(this.base+this.current.fullPath);t?Mt(e):Lt(e)}},e.prototype.getCurrentLocation=function(){return fe(this.base)},e}(te);function fe(t){var e=window.location.pathname,n=e.toLowerCase(),r=t.toLowerCase();return!t||n!==r&&0!==n.indexOf(E(r+"/"))||(e=e.slice(t.length)),(e||"/")+window.location.search+window.location.hash}var pe=function(t){function e(e,n,r){t.call(this,e,n),r&&he(this.base)||de()}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.setupListeners=function(){var t=this;if(!(this.listeners.length>0)){var e=this.router,n=e.options.scrollBehavior,r=Rt&&n;r&&this.listeners.push(Ct());var o=function(){var e=t.current;de()&&t.transitionTo(ve(),(function(n){r&&kt(t.router,n,e,!0),Rt||ge(n.fullPath)}))},i=Rt?"popstate":"hashchange";window.addEventListener(i,o),this.listeners.push((function(){window.removeEventListener(i,o)}))}},e.prototype.push=function(t,e,n){var r=this,o=this,i=o.current;this.transitionTo(t,(function(t){ye(t.fullPath),kt(r.router,t,i,!1),e&&e(t)}),n)},e.prototype.replace=function(t,e,n){var r=this,o=this,i=o.current;this.transitionTo(t,(function(t){ge(t.fullPath),kt(r.router,t,i,!1),e&&e(t)}),n)},e.prototype.go=function(t){window.history.go(t)},e.prototype.ensureURL=function(t){var e=this.current.fullPath;ve()!==e&&(t?ye(e):ge(e))},e.prototype.getCurrentLocation=function(){return ve()},e}(te);function he(t){var e=fe(t);if(!/^\/#/.test(e))return window.location.replace(E(t+"/#"+e)),!0}function de(){var t=ve();return"/"===t.charAt(0)||(ge("/"+t),!1)}function ve(){var t=window.location.href,e=t.indexOf("#");return e<0?"":(t=t.slice(e+1),t)}function me(t){var e=window.location.href,n=e.indexOf("#"),r=n>=0?e.slice(0,n):e;return r+"#"+t}function ye(t){Rt?Mt(me(t)):window.location.hash=t}function ge(t){Rt?Lt(me(t)):window.location.replace(me(t))}var _e=function(t){function e(e,n){t.call(this,e,n),this.stack=[],this.index=-1}return t&&(e.__proto__=t),e.prototype=Object.create(t&&t.prototype),e.prototype.constructor=e,e.prototype.push=function(t,e,n){var r=this;this.transitionTo(t,(function(t){r.stack=r.stack.slice(0,r.index+1).concat(t),r.index++,e&&e(t)}),n)},e.prototype.replace=function(t,e,n){var r=this;this.transitionTo(t,(function(t){r.stack=r.stack.slice(0,r.index).concat(t),e&&e(t)}),n)},e.prototype.go=function(t){var e=this,n=this.index+t;if(!(n<0||n>=this.stack.length)){var r=this.stack[n];this.confirmTransition(r,(function(){var t=e.current;e.index=n,e.updateRoute(r),e.router.afterHooks.forEach((function(e){e&&e(r,t)}))}),(function(t){Kt(t,Dt.duplicated)&&(e.index=n)}))}},e.prototype.getCurrentLocation=function(){var t=this.stack[this.stack.length-1];return t?t.fullPath:"/"},e.prototype.ensureURL=function(){},e}(te),be=function(t){void 0===t&&(t={}),this.app=null,this.apps=[],this.options=t,this.beforeHooks=[],this.resolveHooks=[],this.afterHooks=[],this.matcher=ht(t.routes||[],this);var e=t.mode||"hash";switch(this.fallback="history"===e&&!Rt&&!1!==t.fallback,this.fallback&&(e="hash"),ct||(e="abstract"),this.mode=e,e){case"history":this.history=new le(this,t.base);break;case"hash":this.history=new pe(this,t.base,this.fallback);break;case"abstract":this.history=new _e(this,t.base);break;default:0}},we={currentRoute:{configurable:!0}};function Ce(t,e){return t.push(e),function(){var n=t.indexOf(e);n>-1&&t.splice(n,1)}}function ke(t,e,n){var r="hash"===n?"#"+e:e;return t?E(t+"/"+r):r}be.prototype.match=function(t,e,n){return this.matcher.match(t,e,n)},we.currentRoute.get=function(){return this.history&&this.history.current},be.prototype.init=function(t){var e=this;if(this.apps.push(t),t.$once("hook:destroyed",(function(){var n=e.apps.indexOf(t);n>-1&&e.apps.splice(n,1),e.app===t&&(e.app=e.apps[0]||null),e.app||e.history.teardown()})),!this.app){this.app=t;var n=this.history;if(n instanceof le||n instanceof pe){var r=function(t){var r=n.current,o=e.options.scrollBehavior,i=Rt&&o;i&&"fullPath"in t&&kt(e,t,r,!1)},o=function(t){n.setupListeners(),r(t)};n.transitionTo(n.getCurrentLocation(),o,o)}n.listen((function(t){e.apps.forEach((function(e){e._route=t}))}))}},be.prototype.beforeEach=function(t){return Ce(this.beforeHooks,t)},be.prototype.beforeResolve=function(t){return Ce(this.resolveHooks,t)},be.prototype.afterEach=function(t){return Ce(this.afterHooks,t)},be.prototype.onReady=function(t,e){this.history.onReady(t,e)},be.prototype.onError=function(t){this.history.onError(t)},be.prototype.push=function(t,e,n){var r=this;if(!e&&!n&&"undefined"!==typeof Promise)return new Promise((function(e,n){r.history.push(t,e,n)}));this.history.push(t,e,n)},be.prototype.replace=function(t,e,n){var r=this;if(!e&&!n&&"undefined"!==typeof Promise)return new Promise((function(e,n){r.history.replace(t,e,n)}));this.history.replace(t,e,n)},be.prototype.go=function(t){this.history.go(t)},be.prototype.back=function(){this.go(-1)},be.prototype.forward=function(){this.go(1)},be.prototype.getMatchedComponents=function(t){var e=t?t.matched?t:this.resolve(t).route:this.currentRoute;return e?[].concat.apply([],e.matched.map((function(t){return Object.keys(t.components).map((function(e){return t.components[e]}))}))):[]},be.prototype.resolve=function(t,e,n){e=e||this.history.current;var r=Q(t,e,n,this),o=this.match(r,e),i=o.redirectedFrom||o.fullPath,a=this.history.base,s=ke(a,i,this.mode);return{location:r,route:o,href:s,normalizedTo:r,resolved:o}},be.prototype.getRoutes=function(){return this.matcher.getRoutes()},be.prototype.addRoute=function(t,e){this.matcher.addRoute(t,e),this.history.current!==m&&this.history.transitionTo(this.history.getCurrentLocation())},be.prototype.addRoutes=function(t){this.matcher.addRoutes(t),this.history.current!==m&&this.history.transitionTo(this.history.getCurrentLocation())},Object.defineProperties(be.prototype,we),be.install=st,be.version="3.5.2",be.isNavigationFailure=Kt,be.NavigationFailureType=Dt,be.START_LOCATION=m,ct&&window.Vue&&window.Vue.use(be),e["Z"]=be},144:function(t,e,n){"use strict";n.r(e),n.d(e,{EffectScope:function(){return Oe},computed:function(){return de},customRef:function(){return ie},default:function(){return ni},defineAsyncComponent:function(){return Qn},defineComponent:function(){return mr},del:function(){return Nt},effectScope:function(){return Se},getCurrentInstance:function(){return mt},getCurrentScope:function(){return Ee},h:function(){return Ln},inject:function(){return Ie},isProxy:function(){return Kt},isReactive:function(){return Ht},isReadonly:function(){return qt},isRef:function(){return Xt},isShallow:function(){return zt},markRaw:function(){return Gt},mergeDefaults:function(){return Cn},nextTick:function(){return Jn},onActivated:function(){return cr},onBeforeMount:function(){return nr},onBeforeUnmount:function(){return ar},onBeforeUpdate:function(){return or},onDeactivated:function(){return ur},onErrorCaptured:function(){return dr},onMounted:function(){return rr},onRenderTracked:function(){return fr},onRenderTriggered:function(){return pr},onScopeDispose:function(){return je},onServerPrefetch:function(){return lr},onUnmounted:function(){return sr},onUpdated:function(){return ir},provide:function(){return Ae},proxyRefs:function(){return re},reactive:function(){return Ut},readonly:function(){return le},ref:function(){return Yt},set:function(){return Dt},shallowReactive:function(){return Bt},shallowReadonly:function(){return he},shallowRef:function(){return Qt},toRaw:function(){return Zt},toRef:function(){return se},toRefs:function(){return ae},triggerRef:function(){return ee},unref:function(){return ne},useAttrs:function(){return _n},useCssModule:function(){return Xn},useCssVars:function(){return Yn},useListeners:function(){return bn},useSlots:function(){return gn},version:function(){return vr},watch:function(){return $e},watchEffect:function(){return _e},watchPostEffect:function(){return be},watchSyncEffect:function(){return we}}); +/*! + * Vue.js v2.7.10 + * (c) 2014-2022 Evan You + * Released under the MIT License. + */ +var r=Object.freeze({}),o=Array.isArray;function i(t){return void 0===t||null===t}function a(t){return void 0!==t&&null!==t}function s(t){return!0===t}function c(t){return!1===t}function u(t){return"string"===typeof t||"number"===typeof t||"symbol"===typeof t||"boolean"===typeof t}function l(t){return"function"===typeof t}function f(t){return null!==t&&"object"===typeof t}var p=Object.prototype.toString;function h(t){return"[object Object]"===p.call(t)}function d(t){return"[object RegExp]"===p.call(t)}function v(t){var e=parseFloat(String(t));return e>=0&&Math.floor(e)===e&&isFinite(t)}function m(t){return a(t)&&"function"===typeof t.then&&"function"===typeof t.catch}function y(t){return null==t?"":Array.isArray(t)||h(t)&&t.toString===p?JSON.stringify(t,null,2):String(t)}function g(t){var e=parseFloat(t);return isNaN(e)?t:e}function _(t,e){for(var n=Object.create(null),r=t.split(","),o=0;o-1)return t.splice(n,1)}}var C=Object.prototype.hasOwnProperty;function k(t,e){return C.call(t,e)}function $(t){var e=Object.create(null);return function(n){var r=e[n];return r||(e[n]=t(n))}}var x=/-(\w)/g,O=$((function(t){return t.replace(x,(function(t,e){return e?e.toUpperCase():""}))})),S=$((function(t){return t.charAt(0).toUpperCase()+t.slice(1)})),T=/\B([A-Z])/g,E=$((function(t){return t.replace(T,"-$1").toLowerCase()}));function j(t,e){function n(n){var r=arguments.length;return r?r>1?t.apply(e,arguments):t.call(e,n):t.call(e)}return n._length=t.length,n}function A(t,e){return t.bind(e)}var F=Function.prototype.bind?A:j;function I(t,e){e=e||0;var n=t.length-e,r=new Array(n);while(n--)r[n]=t[n+e];return r}function R(t,e){for(var n in e)t[n]=e[n];return t}function M(t){for(var e={},n=0;n0,rt=tt&&tt.indexOf("edge/")>0;tt&&tt.indexOf("android");var ot=tt&&/iphone|ipad|ipod|ios/.test(tt);tt&&/chrome\/\d+/.test(tt),tt&&/phantomjs/.test(tt);var it,at=tt&&tt.match(/firefox\/(\d+)/),st={}.watch,ct=!1;if(Q)try{var ut={};Object.defineProperty(ut,"passive",{get:function(){ct=!0}}),window.addEventListener("test-passive",null,ut)}catch(nc){}var lt=function(){return void 0===it&&(it=!Q&&"undefined"!==typeof n.g&&(n.g["process"]&&"server"===n.g["process"].env.VUE_ENV)),it},ft=Q&&window.__VUE_DEVTOOLS_GLOBAL_HOOK__;function pt(t){return"function"===typeof t&&/native code/.test(t.toString())}var ht,dt="undefined"!==typeof Symbol&&pt(Symbol)&&"undefined"!==typeof Reflect&&pt(Reflect.ownKeys);ht="undefined"!==typeof Set&&pt(Set)?Set:function(){function t(){this.set=Object.create(null)}return t.prototype.has=function(t){return!0===this.set[t]},t.prototype.add=function(t){this.set[t]=!0},t.prototype.clear=function(){this.set=Object.create(null)},t}();var vt=null;function mt(){return vt&&{proxy:vt}}function yt(t){void 0===t&&(t=null),t||vt&&vt._scope.off(),vt=t,t&&t._scope.on()}var gt=function(){function t(t,e,n,r,o,i,a,s){this.tag=t,this.data=e,this.children=n,this.text=r,this.elm=o,this.ns=void 0,this.context=i,this.fnContext=void 0,this.fnOptions=void 0,this.fnScopeId=void 0,this.key=e&&e.key,this.componentOptions=a,this.componentInstance=void 0,this.parent=void 0,this.raw=!1,this.isStatic=!1,this.isRootInsert=!0,this.isComment=!1,this.isCloned=!1,this.isOnce=!1,this.asyncFactory=s,this.asyncMeta=void 0,this.isAsyncPlaceholder=!1}return Object.defineProperty(t.prototype,"child",{get:function(){return this.componentInstance},enumerable:!1,configurable:!0}),t}(),_t=function(t){void 0===t&&(t="");var e=new gt;return e.text=t,e.isComment=!0,e};function bt(t){return new gt(void 0,void 0,void 0,String(t))}function wt(t){var e=new gt(t.tag,t.data,t.children&&t.children.slice(),t.text,t.elm,t.context,t.componentOptions,t.asyncFactory);return e.ns=t.ns,e.isStatic=t.isStatic,e.key=t.key,e.isComment=t.isComment,e.fnContext=t.fnContext,e.fnOptions=t.fnOptions,e.fnScopeId=t.fnScopeId,e.asyncMeta=t.asyncMeta,e.isCloned=!0,e}var Ct=0,kt=function(){function t(){this.id=Ct++,this.subs=[]}return t.prototype.addSub=function(t){this.subs.push(t)},t.prototype.removeSub=function(t){w(this.subs,t)},t.prototype.depend=function(e){t.target&&t.target.addDep(this)},t.prototype.notify=function(t){var e=this.subs.slice();for(var n=0,r=e.length;n1)return n&&l(e)?e.call(r):e}else 0}var Re=$((function(t){var e="&"===t.charAt(0);t=e?t.slice(1):t;var n="~"===t.charAt(0);t=n?t.slice(1):t;var r="!"===t.charAt(0);return t=r?t.slice(1):t,{name:t,once:n,capture:r,passive:e}}));function Me(t,e){function n(){var t=n.fns;if(!o(t))return Dn(t,null,arguments,e,"v-on handler");for(var r=t.slice(),i=0;i0&&(r=We(r,"".concat(e||"","_").concat(n)),Be(r[0])&&Be(l)&&(f[c]=bt(l.text+r[0].text),r.shift()),f.push.apply(f,r)):u(r)?Be(l)?f[c]=bt(l.text+r):""!==r&&f.push(bt(r)):Be(r)&&Be(l)?f[c]=bt(l.text+r.text):(s(t._isVList)&&a(r.tag)&&i(r.key)&&a(e)&&(r.key="__vlist".concat(e,"_").concat(n,"__")),f.push(r)));return f}function He(t,e){var n,r,i,s,c=null;if(o(t)||"string"===typeof t)for(c=new Array(t.length),n=0,r=t.length;n0,s=e?!!e.$stable:!a,c=e&&e.$key;if(e){if(e._normalized)return e._normalized;if(s&&o&&o!==r&&c===o.$key&&!a&&!o.$hasNormal)return o;for(var u in i={},e)e[u]&&"$"!==u[0]&&(i[u]=ln(t,n,u,e[u]))}else i={};for(var l in n)l in i||(i[l]=fn(n,l));return e&&Object.isExtensible(e)&&(e._normalized=i),G(i,"$stable",s),G(i,"$key",c),G(i,"$hasNormal",a),i}function ln(t,e,n,r){var i=function(){var e=vt;yt(t);var n=arguments.length?r.apply(null,arguments):r({});n=n&&"object"===typeof n&&!o(n)?[n]:Ue(n);var i=n&&n[0];return yt(e),n&&(!i||1===n.length&&i.isComment&&!cn(i))?void 0:n};return r.proxy&&Object.defineProperty(e,n,{get:i,enumerable:!0,configurable:!0}),i}function fn(t,e){return function(){return t[e]}}function pn(t){var e=t.$options,n=e.setup;if(n){var r=t._setupContext=hn(t);yt(t),xt();var o=Dn(n,null,[t._props||Bt({}),r],t,"setup");if(Ot(),yt(),l(o))e.render=o;else if(f(o))if(t._setupState=o,o.__sfc){var i=t._setupProxy={};for(var a in o)"__sfc"!==a&&oe(i,o,a)}else for(var a in o)Z(a)||oe(t,o,a);else 0}}function hn(t){return{get attrs(){if(!t._attrsProxy){var e=t._attrsProxy={};G(e,"_v_attr_proxy",!0),dn(e,t.$attrs,r,t,"$attrs")}return t._attrsProxy},get listeners(){if(!t._listenersProxy){var e=t._listenersProxy={};dn(e,t.$listeners,r,t,"$listeners")}return t._listenersProxy},get slots(){return mn(t)},emit:F(t.$emit,t),expose:function(e){e&&Object.keys(e).forEach((function(n){return oe(t,e,n)}))}}}function dn(t,e,n,r,o){var i=!1;for(var a in e)a in t?e[a]!==n[a]&&(i=!0):(i=!0,vn(t,a,r,o));for(var a in t)a in e||(i=!0,delete t[a]);return i}function vn(t,e,n,r){Object.defineProperty(t,e,{enumerable:!0,configurable:!0,get:function(){return n[r][e]}})}function mn(t){return t._slotsProxy||yn(t._slotsProxy={},t.$scopedSlots),t._slotsProxy}function yn(t,e){for(var n in e)t[n]=e[n];for(var n in t)n in e||delete t[n]}function gn(){return wn().slots}function _n(){return wn().attrs}function bn(){return wn().listeners}function wn(){var t=vt;return t._setupContext||(t._setupContext=hn(t))}function Cn(t,e){var n=o(t)?t.reduce((function(t,e){return t[e]={},t}),{}):t;for(var r in e){var i=n[r];i?o(i)||l(i)?n[r]={type:i,default:e[r]}:i.default=e[r]:null===i&&(n[r]={default:e[r]})}return n}function kn(t){t._vnode=null,t._staticTrees=null;var e=t.$options,n=t.$vnode=e._parentVnode,o=n&&n.context;t.$slots=an(e._renderChildren,o),t.$scopedSlots=n?un(t.$parent,n.data.scopedSlots,t.$slots):r,t._c=function(e,n,r,o){return Fn(t,e,n,r,o,!1)},t.$createElement=function(e,n,r,o){return Fn(t,e,n,r,o,!0)};var i=n&&n.data;Pt(t,"$attrs",i&&i.attrs||r,null,!0),Pt(t,"$listeners",e._parentListeners||r,null,!0)}var $n=null;function xn(t){on(t.prototype),t.prototype.$nextTick=function(t){return Jn(t,this)},t.prototype._render=function(){var t,e=this,n=e.$options,r=n.render,i=n._parentVnode;i&&e._isMounted&&(e.$scopedSlots=un(e.$parent,i.data.scopedSlots,e.$slots,e.$scopedSlots),e._slotsProxy&&yn(e._slotsProxy,e.$scopedSlots)),e.$vnode=i;try{yt(e),$n=e,t=r.call(e._renderProxy,e.$createElement)}catch(nc){Pn(nc,e,"render"),t=e._vnode}finally{$n=null,yt()}return o(t)&&1===t.length&&(t=t[0]),t instanceof gt||(t=_t()),t.parent=i,t}}function On(t,e){return(t.__esModule||dt&&"Module"===t[Symbol.toStringTag])&&(t=t.default),f(t)?e.extend(t):t}function Sn(t,e,n,r,o){var i=_t();return i.asyncFactory=t,i.asyncMeta={data:e,context:n,children:r,tag:o},i}function Tn(t,e){if(s(t.error)&&a(t.errorComp))return t.errorComp;if(a(t.resolved))return t.resolved;var n=$n;if(n&&a(t.owners)&&-1===t.owners.indexOf(n)&&t.owners.push(n),s(t.loading)&&a(t.loadingComp))return t.loadingComp;if(n&&!a(t.owners)){var r=t.owners=[n],o=!0,c=null,u=null;n.$on("hook:destroyed",(function(){return w(r,n)}));var l=function(t){for(var e=0,n=r.length;e1?I(n):n;for(var r=I(arguments,1),o='event handler for "'.concat(t,'"'),i=0,a=n.length;idocument.createEvent("Event").timeStamp&&(Kr=function(){return Zr.now()})}var Gr=function(t,e){if(t.post){if(!e.post)return 1}else if(e.post)return-1;return t.id-e.id};function Jr(){var t,e;for(qr=Kr(),Wr=!0,Nr.sort(Gr),Hr=0;HrHr&&Nr[n].id>t.id)n--;Nr.splice(n+1,0,t)}else Nr.push(t);Br||(Br=!0,Jn(Jr))}}function eo(t){var e=t.$options.provide;if(e){var n=l(e)?e.call(t):e;if(!f(n))return;for(var r=Fe(t),o=dt?Reflect.ownKeys(n):Object.keys(n),i=0;i-1)if(i&&!k(o,"default"))a=!1;else if(""===a||a===E(t)){var c=Mo(String,o.type);(c<0||s-1)return this;var n=I(arguments,1);return n.unshift(this),l(t.install)?t.install.apply(t,n):l(t)&&t.apply(null,n),e.push(t),this}}function oi(t){t.mixin=function(t){return this.options=To(this.options,t),this}}function ii(t){t.cid=0;var e=1;t.extend=function(t){t=t||{};var n=this,r=n.cid,o=t._Ctor||(t._Ctor={});if(o[r])return o[r];var i=co(t)||co(n.options);var a=function(t){this._init(t)};return a.prototype=Object.create(n.prototype),a.prototype.constructor=a,a.cid=e++,a.options=To(n.options,t),a["super"]=n,a.options.props&&ai(a),a.options.computed&&si(a),a.extend=n.extend,a.mixin=n.mixin,a.use=n.use,H.forEach((function(t){a[t]=n[t]})),i&&(a.options.components[i]=a),a.superOptions=n.options,a.extendOptions=t,a.sealedOptions=R({},a.options),o[r]=a,a}}function ai(t){var e=t.options.props;for(var n in e)Po(t.prototype,"_props",n)}function si(t){var e=t.options.computed;for(var n in e)Ho(t.prototype,n,e[n])}function ci(t){H.forEach((function(e){t[e]=function(t,n){return n?("component"===e&&h(n)&&(n.name=n.name||t,n=this.options._base.extend(n)),"directive"===e&&l(n)&&(n={bind:n,update:n}),this.options[e+"s"][t]=n,n):this.options[e+"s"][t]}}))}function ui(t){return t&&(co(t.Ctor.options)||t.tag)}function li(t,e){return o(t)?t.indexOf(e)>-1:"string"===typeof t?t.split(",").indexOf(e)>-1:!!d(t)&&t.test(e)}function fi(t,e){var n=t.cache,r=t.keys,o=t._vnode;for(var i in n){var a=n[i];if(a){var s=a.name;s&&!e(s)&&pi(n,i,r,o)}}}function pi(t,e,n,r){var o=t[e];!o||r&&o.tag===r.tag||o.componentInstance.$destroy(),t[e]=null,w(n,e)}Yo(ni),Jo(ni),Tr(ni),Fr(ni),xn(ni);var hi=[String,RegExp,Array],di={name:"keep-alive",abstract:!0,props:{include:hi,exclude:hi,max:[String,Number]},methods:{cacheVNode:function(){var t=this,e=t.cache,n=t.keys,r=t.vnodeToCache,o=t.keyToCache;if(r){var i=r.tag,a=r.componentInstance,s=r.componentOptions;e[o]={name:ui(s),tag:i,componentInstance:a},n.push(o),this.max&&n.length>parseInt(this.max)&&pi(e,n[0],n,this._vnode),this.vnodeToCache=null}}},created:function(){this.cache=Object.create(null),this.keys=[]},destroyed:function(){for(var t in this.cache)pi(this.cache,t,this.keys)},mounted:function(){var t=this;this.cacheVNode(),this.$watch("include",(function(e){fi(t,(function(t){return li(e,t)}))})),this.$watch("exclude",(function(e){fi(t,(function(t){return!li(e,t)}))}))},updated:function(){this.cacheVNode()},render:function(){var t=this.$slots.default,e=En(t),n=e&&e.componentOptions;if(n){var r=ui(n),o=this,i=o.include,a=o.exclude;if(i&&(!r||!li(i,r))||a&&r&&li(a,r))return e;var s=this,c=s.cache,u=s.keys,l=null==e.key?n.Ctor.cid+(n.tag?"::".concat(n.tag):""):e.key;c[l]?(e.componentInstance=c[l].componentInstance,w(u,l),u.push(l)):(this.vnodeToCache=e,this.keyToCache=l),e.data.keepAlive=!0}return e||t&&t[0]}},vi={KeepAlive:di};function mi(t){var e={get:function(){return q}};Object.defineProperty(t,"config",e),t.util={warn:yo,extend:R,mergeOptions:To,defineReactive:Pt},t.set=Dt,t.delete=Nt,t.nextTick=Jn,t.observable=function(t){return Lt(t),t},t.options=Object.create(null),H.forEach((function(e){t.options[e+"s"]=Object.create(null)})),t.options._base=t,R(t.options.components,vi),ri(t),oi(t),ii(t),ci(t)}mi(ni),Object.defineProperty(ni.prototype,"$isServer",{get:lt}),Object.defineProperty(ni.prototype,"$ssrContext",{get:function(){return this.$vnode&&this.$vnode.ssrContext}}),Object.defineProperty(ni,"FunctionalRenderContext",{value:oo}),ni.version=vr;var yi=_("style,class"),gi=_("input,textarea,option,select,progress"),_i=function(t,e,n){return"value"===n&&gi(t)&&"button"!==e||"selected"===n&&"option"===t||"checked"===n&&"input"===t||"muted"===n&&"video"===t},bi=_("contenteditable,draggable,spellcheck"),wi=_("events,caret,typing,plaintext-only"),Ci=function(t,e){return Si(e)||"false"===e?"false":"contenteditable"===t&&wi(e)?e:"true"},ki=_("allowfullscreen,async,autofocus,autoplay,checked,compact,controls,declare,default,defaultchecked,defaultmuted,defaultselected,defer,disabled,enabled,formnovalidate,hidden,indeterminate,inert,ismap,itemscope,loop,multiple,muted,nohref,noresize,noshade,novalidate,nowrap,open,pauseonexit,readonly,required,reversed,scoped,seamless,selected,sortable,truespeed,typemustmatch,visible"),$i="http://www.w3.org/1999/xlink",xi=function(t){return":"===t.charAt(5)&&"xlink"===t.slice(0,5)},Oi=function(t){return xi(t)?t.slice(6,t.length):""},Si=function(t){return null==t||!1===t};function Ti(t){var e=t.data,n=t,r=t;while(a(r.componentInstance))r=r.componentInstance._vnode,r&&r.data&&(e=Ei(r.data,e));while(a(n=n.parent))n&&n.data&&(e=Ei(e,n.data));return ji(e.staticClass,e.class)}function Ei(t,e){return{staticClass:Ai(t.staticClass,e.staticClass),class:a(t.class)?[t.class,e.class]:e.class}}function ji(t,e){return a(t)||a(e)?Ai(t,Fi(e)):""}function Ai(t,e){return t?e?t+" "+e:t:e||""}function Fi(t){return Array.isArray(t)?Ii(t):f(t)?Ri(t):"string"===typeof t?t:""}function Ii(t){for(var e,n="",r=0,o=t.length;r-1?Vi[t]=e.constructor===window.HTMLUnknownElement||e.constructor===window.HTMLElement:Vi[t]=/HTMLUnknownElement/.test(e.toString())}var Bi=_("text,number,password,search,email,tel,url");function Wi(t){if("string"===typeof t){var e=document.querySelector(t);return e||document.createElement("div")}return t}function Hi(t,e){var n=document.createElement(t);return"select"!==t||e.data&&e.data.attrs&&void 0!==e.data.attrs.multiple&&n.setAttribute("multiple","multiple"),n}function zi(t,e){return document.createElementNS(Mi[t],e)}function qi(t){return document.createTextNode(t)}function Ki(t){return document.createComment(t)}function Zi(t,e,n){t.insertBefore(e,n)}function Gi(t,e){t.removeChild(e)}function Ji(t,e){t.appendChild(e)}function Xi(t){return t.parentNode}function Yi(t){return t.nextSibling}function Qi(t){return t.tagName}function ta(t,e){t.textContent=e}function ea(t,e){t.setAttribute(e,"")}var na=Object.freeze({__proto__:null,createElement:Hi,createElementNS:zi,createTextNode:qi,createComment:Ki,insertBefore:Zi,removeChild:Gi,appendChild:Ji,parentNode:Xi,nextSibling:Yi,tagName:Qi,setTextContent:ta,setStyleScope:ea}),ra={create:function(t,e){oa(e)},update:function(t,e){t.data.ref!==e.data.ref&&(oa(t,!0),oa(e))},destroy:function(t){oa(t,!0)}};function oa(t,e){var n=t.data.ref;if(a(n)){var r=t.context,i=t.componentInstance||t.elm,s=e?null:i,c=e?void 0:i;if(l(n))Dn(n,r,[s],r,"template ref function");else{var u=t.data.refInFor,f="string"===typeof n||"number"===typeof n,p=Xt(n),h=r.$refs;if(f||p)if(u){var d=f?h[n]:n.value;e?o(d)&&w(d,i):o(d)?d.includes(i)||d.push(i):f?(h[n]=[i],ia(r,n,h[n])):n.value=[i]}else if(f){if(e&&h[n]!==i)return;h[n]=c,ia(r,n,s)}else if(p){if(e&&n.value!==i)return;n.value=s}else 0}}}function ia(t,e,n){var r=t._setupState;r&&k(r,e)&&(Xt(r[e])?r[e].value=n:r[e]=n)}var aa=new gt("",{},[]),sa=["create","activate","update","remove","destroy"];function ca(t,e){return t.key===e.key&&t.asyncFactory===e.asyncFactory&&(t.tag===e.tag&&t.isComment===e.isComment&&a(t.data)===a(e.data)&&ua(t,e)||s(t.isAsyncPlaceholder)&&i(e.asyncFactory.error))}function ua(t,e){if("input"!==t.tag)return!0;var n,r=a(n=t.data)&&a(n=n.attrs)&&n.type,o=a(n=e.data)&&a(n=n.attrs)&&n.type;return r===o||Bi(r)&&Bi(o)}function la(t,e,n){var r,o,i={};for(r=e;r<=n;++r)o=t[r].key,a(o)&&(i[o]=r);return i}function fa(t){var e,n,r={},c=t.modules,l=t.nodeOps;for(e=0;ev?(f=i(n[g+1])?null:n[g+1].elm,$(t,f,n,h,g,r)):h>g&&O(e,p,v)}function E(t,e,n,r){for(var o=n;o-1?Ca(t,e,n):ki(e)?Si(n)?t.removeAttribute(e):(n="allowfullscreen"===e&&"EMBED"===t.tagName?"true":e,t.setAttribute(e,n)):bi(e)?t.setAttribute(e,Ci(e,n)):xi(e)?Si(n)?t.removeAttributeNS($i,Oi(e)):t.setAttributeNS($i,e,n):Ca(t,e,n)}function Ca(t,e,n){if(Si(n))t.removeAttribute(e);else{if(et&&!nt&&"TEXTAREA"===t.tagName&&"placeholder"===e&&""!==n&&!t.__ieph){var r=function(e){e.stopImmediatePropagation(),t.removeEventListener("input",r)};t.addEventListener("input",r),t.__ieph=!0}t.setAttribute(e,n)}}var ka={create:ba,update:ba};function $a(t,e){var n=e.elm,r=e.data,o=t.data;if(!(i(r.staticClass)&&i(r.class)&&(i(o)||i(o.staticClass)&&i(o.class)))){var s=Ti(e),c=n._transitionClasses;a(c)&&(s=Ai(s,Fi(c))),s!==n._prevClass&&(n.setAttribute("class",s),n._prevClass=s)}}var xa,Oa={create:$a,update:$a},Sa="__r",Ta="__c";function Ea(t){if(a(t[Sa])){var e=et?"change":"input";t[e]=[].concat(t[Sa],t[e]||[]),delete t[Sa]}a(t[Ta])&&(t.change=[].concat(t[Ta],t.change||[]),delete t[Ta])}function ja(t,e,n){var r=xa;return function o(){var i=e.apply(null,arguments);null!==i&&Ia(t,o,n,r)}}var Aa=Bn&&!(at&&Number(at[1])<=53);function Fa(t,e,n,r){if(Aa){var o=qr,i=e;e=i._wrapper=function(t){if(t.target===t.currentTarget||t.timeStamp>=o||t.timeStamp<=0||t.target.ownerDocument!==document)return i.apply(this,arguments)}}xa.addEventListener(t,e,ct?{capture:n,passive:r}:n)}function Ia(t,e,n,r){(r||xa).removeEventListener(t,e._wrapper||e,n)}function Ra(t,e){if(!i(t.data.on)||!i(e.data.on)){var n=e.data.on||{},r=t.data.on||{};xa=e.elm||t.elm,Ea(n),Le(n,r,Fa,Ia,ja,e.context),xa=void 0}}var Ma,La={create:Ra,update:Ra,destroy:function(t){return Ra(t,aa)}};function Pa(t,e){if(!i(t.data.domProps)||!i(e.data.domProps)){var n,r,o=e.elm,c=t.data.domProps||{},u=e.data.domProps||{};for(n in(a(u.__ob__)||s(u._v_attr_proxy))&&(u=e.data.domProps=R({},u)),c)n in u||(o[n]="");for(n in u){if(r=u[n],"textContent"===n||"innerHTML"===n){if(e.children&&(e.children.length=0),r===c[n])continue;1===o.childNodes.length&&o.removeChild(o.childNodes[0])}if("value"===n&&"PROGRESS"!==o.tagName){o._value=r;var l=i(r)?"":String(r);Da(o,l)&&(o.value=l)}else if("innerHTML"===n&&Pi(o.tagName)&&i(o.innerHTML)){Ma=Ma||document.createElement("div"),Ma.innerHTML="".concat(r,"");var f=Ma.firstChild;while(o.firstChild)o.removeChild(o.firstChild);while(f.firstChild)o.appendChild(f.firstChild)}else if(r!==c[n])try{o[n]=r}catch(nc){}}}}function Da(t,e){return!t.composing&&("OPTION"===t.tagName||Na(t,e)||Va(t,e))}function Na(t,e){var n=!0;try{n=document.activeElement!==t}catch(nc){}return n&&t.value!==e}function Va(t,e){var n=t.value,r=t._vModifiers;if(a(r)){if(r.number)return g(n)!==g(e);if(r.trim)return n.trim()!==e.trim()}return n!==e}var Ua={create:Pa,update:Pa},Ba=$((function(t){var e={},n=/;(?![^(]*\))/g,r=/:(.+)/;return t.split(n).forEach((function(t){if(t){var n=t.split(r);n.length>1&&(e[n[0].trim()]=n[1].trim())}})),e}));function Wa(t){var e=Ha(t.style);return t.staticStyle?R(t.staticStyle,e):e}function Ha(t){return Array.isArray(t)?M(t):"string"===typeof t?Ba(t):t}function za(t,e){var n,r={};if(e){var o=t;while(o.componentInstance)o=o.componentInstance._vnode,o&&o.data&&(n=Wa(o.data))&&R(r,n)}(n=Wa(t.data))&&R(r,n);var i=t;while(i=i.parent)i.data&&(n=Wa(i.data))&&R(r,n);return r}var qa,Ka=/^--/,Za=/\s*!important$/,Ga=function(t,e,n){if(Ka.test(e))t.style.setProperty(e,n);else if(Za.test(n))t.style.setProperty(E(e),n.replace(Za,""),"important");else{var r=Xa(e);if(Array.isArray(n))for(var o=0,i=n.length;o-1?e.split(ts).forEach((function(e){return t.classList.add(e)})):t.classList.add(e);else{var n=" ".concat(t.getAttribute("class")||""," ");n.indexOf(" "+e+" ")<0&&t.setAttribute("class",(n+e).trim())}}function ns(t,e){if(e&&(e=e.trim()))if(t.classList)e.indexOf(" ")>-1?e.split(ts).forEach((function(e){return t.classList.remove(e)})):t.classList.remove(e),t.classList.length||t.removeAttribute("class");else{var n=" ".concat(t.getAttribute("class")||""," "),r=" "+e+" ";while(n.indexOf(r)>=0)n=n.replace(r," ");n=n.trim(),n?t.setAttribute("class",n):t.removeAttribute("class")}}function rs(t){if(t){if("object"===typeof t){var e={};return!1!==t.css&&R(e,os(t.name||"v")),R(e,t),e}return"string"===typeof t?os(t):void 0}}var os=$((function(t){return{enterClass:"".concat(t,"-enter"),enterToClass:"".concat(t,"-enter-to"),enterActiveClass:"".concat(t,"-enter-active"),leaveClass:"".concat(t,"-leave"),leaveToClass:"".concat(t,"-leave-to"),leaveActiveClass:"".concat(t,"-leave-active")}})),is=Q&&!nt,as="transition",ss="animation",cs="transition",us="transitionend",ls="animation",fs="animationend";is&&(void 0===window.ontransitionend&&void 0!==window.onwebkittransitionend&&(cs="WebkitTransition",us="webkitTransitionEnd"),void 0===window.onanimationend&&void 0!==window.onwebkitanimationend&&(ls="WebkitAnimation",fs="webkitAnimationEnd"));var ps=Q?window.requestAnimationFrame?window.requestAnimationFrame.bind(window):setTimeout:function(t){return t()};function hs(t){ps((function(){ps(t)}))}function ds(t,e){var n=t._transitionClasses||(t._transitionClasses=[]);n.indexOf(e)<0&&(n.push(e),es(t,e))}function vs(t,e){t._transitionClasses&&w(t._transitionClasses,e),ns(t,e)}function ms(t,e,n){var r=gs(t,e),o=r.type,i=r.timeout,a=r.propCount;if(!o)return n();var s=o===as?us:fs,c=0,u=function(){t.removeEventListener(s,l),n()},l=function(e){e.target===t&&++c>=a&&u()};setTimeout((function(){c0&&(n=as,l=a,f=i.length):e===ss?u>0&&(n=ss,l=u,f=c.length):(l=Math.max(a,u),n=l>0?a>u?as:ss:null,f=n?n===as?i.length:c.length:0);var p=n===as&&ys.test(r[cs+"Property"]);return{type:n,timeout:l,propCount:f,hasTransform:p}}function _s(t,e){while(t.length1}function xs(t,e){!0!==e.data.show&&ws(e)}var Os=Q?{create:xs,activate:xs,remove:function(t,e){!0!==t.data.show?Cs(t,e):e()}}:{},Ss=[ka,Oa,La,Ua,Qa,Os],Ts=Ss.concat(_a),Es=fa({nodeOps:na,modules:Ts});nt&&document.addEventListener("selectionchange",(function(){var t=document.activeElement;t&&t.vmodel&&Ps(t,"input")}));var js={inserted:function(t,e,n,r){"select"===n.tag?(r.elm&&!r.elm._vOptions?Pe(n,"postpatch",(function(){js.componentUpdated(t,e,n)})):As(t,e,n.context),t._vOptions=[].map.call(t.options,Rs)):("textarea"===n.tag||Bi(t.type))&&(t._vModifiers=e.modifiers,e.modifiers.lazy||(t.addEventListener("compositionstart",Ms),t.addEventListener("compositionend",Ls),t.addEventListener("change",Ls),nt&&(t.vmodel=!0)))},componentUpdated:function(t,e,n){if("select"===n.tag){As(t,e,n.context);var r=t._vOptions,o=t._vOptions=[].map.call(t.options,Rs);if(o.some((function(t,e){return!N(t,r[e])}))){var i=t.multiple?e.value.some((function(t){return Is(t,o)})):e.value!==e.oldValue&&Is(e.value,o);i&&Ps(t,"change")}}}};function As(t,e,n){Fs(t,e,n),(et||rt)&&setTimeout((function(){Fs(t,e,n)}),0)}function Fs(t,e,n){var r=e.value,o=t.multiple;if(!o||Array.isArray(r)){for(var i,a,s=0,c=t.options.length;s-1,a.selected!==i&&(a.selected=i);else if(N(Rs(a),r))return void(t.selectedIndex!==s&&(t.selectedIndex=s));o||(t.selectedIndex=-1)}}function Is(t,e){return e.every((function(e){return!N(e,t)}))}function Rs(t){return"_value"in t?t._value:t.value}function Ms(t){t.target.composing=!0}function Ls(t){t.target.composing&&(t.target.composing=!1,Ps(t.target,"input"))}function Ps(t,e){var n=document.createEvent("HTMLEvents");n.initEvent(e,!0,!0),t.dispatchEvent(n)}function Ds(t){return!t.componentInstance||t.data&&t.data.transition?t:Ds(t.componentInstance._vnode)}var Ns={bind:function(t,e,n){var r=e.value;n=Ds(n);var o=n.data&&n.data.transition,i=t.__vOriginalDisplay="none"===t.style.display?"":t.style.display;r&&o?(n.data.show=!0,ws(n,(function(){t.style.display=i}))):t.style.display=r?i:"none"},update:function(t,e,n){var r=e.value,o=e.oldValue;if(!r!==!o){n=Ds(n);var i=n.data&&n.data.transition;i?(n.data.show=!0,r?ws(n,(function(){t.style.display=t.__vOriginalDisplay})):Cs(n,(function(){t.style.display="none"}))):t.style.display=r?t.__vOriginalDisplay:"none"}},unbind:function(t,e,n,r,o){o||(t.style.display=t.__vOriginalDisplay)}},Vs={model:js,show:Ns},Us={name:String,appear:Boolean,css:Boolean,mode:String,type:String,enterClass:String,leaveClass:String,enterToClass:String,leaveToClass:String,enterActiveClass:String,leaveActiveClass:String,appearClass:String,appearActiveClass:String,appearToClass:String,duration:[Number,String,Object]};function Bs(t){var e=t&&t.componentOptions;return e&&e.Ctor.options.abstract?Bs(En(e.children)):t}function Ws(t){var e={},n=t.$options;for(var r in n.propsData)e[r]=t[r];var o=n._parentListeners;for(var r in o)e[O(r)]=o[r];return e}function Hs(t,e){if(/\d-keep-alive$/.test(e.tag))return t("keep-alive",{props:e.componentOptions.propsData})}function zs(t){while(t=t.parent)if(t.data.transition)return!0}function qs(t,e){return e.key===t.key&&e.tag===t.tag}var Ks=function(t){return t.tag||cn(t)},Zs=function(t){return"show"===t.name},Gs={name:"transition",props:Us,abstract:!0,render:function(t){var e=this,n=this.$slots.default;if(n&&(n=n.filter(Ks),n.length)){0;var r=this.mode;0;var o=n[0];if(zs(this.$vnode))return o;var i=Bs(o);if(!i)return o;if(this._leaving)return Hs(t,o);var a="__transition-".concat(this._uid,"-");i.key=null==i.key?i.isComment?a+"comment":a+i.tag:u(i.key)?0===String(i.key).indexOf(a)?i.key:a+i.key:i.key;var s=(i.data||(i.data={})).transition=Ws(this),c=this._vnode,l=Bs(c);if(i.data.directives&&i.data.directives.some(Zs)&&(i.data.show=!0),l&&l.data&&!qs(i,l)&&!cn(l)&&(!l.componentInstance||!l.componentInstance._vnode.isComment)){var f=l.data.transition=R({},s);if("out-in"===r)return this._leaving=!0,Pe(f,"afterLeave",(function(){e._leaving=!1,e.$forceUpdate()})),Hs(t,o);if("in-out"===r){if(cn(i))return c;var p,h=function(){p()};Pe(s,"afterEnter",h),Pe(s,"enterCancelled",h),Pe(f,"delayLeave",(function(t){p=t}))}}return o}}},Js=R({tag:String,moveClass:String},Us);delete Js.mode;var Xs={props:Js,beforeMount:function(){var t=this,e=this._update;this._update=function(n,r){var o=jr(t);t.__patch__(t._vnode,t.kept,!1,!0),t._vnode=t.kept,o(),e.call(t,n,r)}},render:function(t){for(var e=this.tag||this.$vnode.data.tag||"span",n=Object.create(null),r=this.prevChildren=this.children,o=this.$slots.default||[],i=this.children=[],a=Ws(this),s=0;s{e.style.height=n}))},leave(e){const{height:t}=getComputedStyle(e);e.style.height=t,getComputedStyle(e).height,requestAnimationFrame((()=>{e.style.height=0}))}}};return e("transition",n,t.children)}},r=a,o=n(1001),l=(0,o.Z)(r,i,s,!1,null,null,null),c=l.exports},8590:function(e,t,n){"use strict";n.r(t),n.d(t,{default:function(){return uh}});var i=function(){var e=this,t=e._self._c;return t("CodeTheme",[e.topicData?t("DocumentationLayout",e._b({scopedSlots:e._u([{key:"nav-title",fn:function({className:n}){return[t(e.rootLink?"router-link":"h2",{tag:"component",class:n,attrs:{to:e.rootLink}},[e._v(" "+e._s(e.$t("documentation.title"))+" ")])]}},{key:"content",fn:function(){return[t("Topic",e._b({key:e.topicKey,attrs:{disableHeroBackground:e.disableHeroBackground,objcPath:e.objcPath,swiftPath:e.swiftPath,isSymbolDeprecated:e.isSymbolDeprecated,isSymbolBeta:e.isSymbolBeta,languagePaths:e.languagePaths,enableOnThisPageNav:e.enableOnThisPageNav,enableMinimized:e.enableMinimized,hierarchyItems:e.hierarchyItems}},"Topic",e.topicProps,!1))]},proxy:!0}],null,!1,402783128)},"DocumentationLayout",e.documentationLayoutProps,!1)):e._e()],1)},s=[];const a="/";function r(e){return e.replace(/~[0,1]/g,(e=>({"~0":"~","~1":"/"}[e]||e)))}function*o(e){const t=1;if(e.lengtht)throw new Error(`invalid array index ${e}`);return n}function*p(e,t,n={strict:!1}){let i=e;for(const s of o(t)){if(n.strict&&!Object.prototype.hasOwnProperty.call(i,s))throw new u(t);i=i[s],yield{node:i,token:s}}}function g(e,t){let n=e;for(const{node:i}of p(e,t,{strict:!0}))n=i;return n}function f(e,t,n){let i=null,s=e,a=null;for(const{node:o,token:l}of p(e,t))i=s,s=o,a=l;if(!i)throw new u(t);if(Array.isArray(i))try{const e=h(a,i);i.splice(e,0,n)}catch(r){throw new u(t)}else Object.assign(i,{[a]:n});return e}function m(e,t){let n=null,i=e,s=null;for(const{node:r,token:o}of p(e,t))n=i,i=r,s=o;if(!n)throw new u(t);if(Array.isArray(n))try{const e=h(s,n);n.splice(e,1)}catch(a){throw new u(t)}else{if(!i)throw new u(t);delete n[s]}return e}function y(e,t,n){return m(e,t),f(e,t,n),e}function v(e,t,n){const i=g(e,t);return m(e,t),f(e,n,i),e}function b(e,t,n){return f(e,n,g(e,t)),e}function T(e,t,n){function i(e,t){const n=typeof e,s=typeof t;if(n!==s)return!1;switch(n){case d:{const n=Object.keys(e),s=Object.keys(t);return n.length===s.length&&n.every(((n,a)=>n===s[a]&&i(e[n],t[n])))}default:return e===t}}const s=g(e,t);if(!i(n,s))throw new Error("test failed");return e}const _={add:(e,{path:t,value:n})=>f(e,t,n),copy:(e,{from:t,path:n})=>b(e,t,n),move:(e,{from:t,path:n})=>v(e,t,n),remove:(e,{path:t})=>m(e,t),replace:(e,{path:t,value:n})=>y(e,t,n),test:(e,{path:t,value:n})=>T(e,t,n)};function S(e,{op:t,...n}){const i=_[t];if(!i)throw new Error("unknown operation");return i(e,n)}function k(e,t){return t.reduce(S,e)}var C=n(7192),w=n(9089),x=n(8841),I=function(){var e=this,t=e._self._c;return t("div",{staticClass:"doc-topic",class:{"with-on-this-page":e.enableOnThisPageNav&&e.isOnThisPageNavVisible}},[t(e.isTargetIDE?"div":"main",{tag:"component",staticClass:"main",attrs:{id:"main"}},[t("DocumentationHero",{attrs:{role:e.role,enhanceBackground:e.enhanceBackground,enableMinimized:e.enableMinimized,shortHero:e.shortHero,shouldShowLanguageSwitcher:e.shouldShowLanguageSwitcher,iconOverride:e.references[e.pageIcon],standardColorIdentifier:e.standardColorIdentifier},scopedSlots:e._u([{key:"above-content",fn:function(){return[e._t("above-hero-content")]},proxy:!0}],null,!0)},[e._t("above-title"),!e.parentTopics.length||e.enableMinimized||e.isTargetIDE?e._e():t("Hierarchy",{attrs:{currentTopicTitle:e.title,isSymbolDeprecated:e.isSymbolDeprecated,isSymbolBeta:e.isSymbolBeta,parentTopics:e.parentTopics,currentTopicTags:e.tags}}),e.shouldShowLanguageSwitcher?t("LanguageSwitcher",{attrs:{interfaceLanguage:e.interfaceLanguage,objcPath:e.objcPath,swiftPath:e.swiftPath}}):e._e(),t("Title",{class:{"minimized-title":e.enableMinimized},attrs:{eyebrow:e.enableMinimized?null:e.roleHeading},scopedSlots:e._u([e.isSymbolDeprecated||e.isSymbolBeta?{key:"after",fn:function(){return[t("small",{class:e.tagName,attrs:{"data-tag-name":e.tagName}})]},proxy:!0}:null],null,!0)},[t(e.titleBreakComponent,{tag:"component"},[e._v(e._s(e.title))])],1),e.abstract?t("Abstract",{class:{"minimized-abstract":e.enableMinimized},attrs:{content:e.abstract}}):e._e(),e.sampleCodeDownload?t("div",[t("DownloadButton",{staticClass:"sample-download",attrs:{action:e.sampleCodeDownload.action}})],1):e._e(),e.shouldShowAvailability?t("Availability",{attrs:{platforms:e.platforms,technologies:e.technologies}}):e._e(),e.declarations.length?t("div",{staticClass:"declarations-container",class:{"minimized-container":e.enableMinimized}},e._l(e.declarations,(function(n,i){return t("Declaration",{key:i,attrs:{conformance:e.conformance,declarations:n.declarations,source:e.remoteSource,declListExpanded:e.declListExpanded},on:{"update:declListExpanded":function(t){e.declListExpanded=t},"update:decl-list-expanded":function(t){e.declListExpanded=t}}})})),1):e._e()],2),t("div",{staticClass:"doc-content-wrapper"},[t("div",{staticClass:"doc-content",class:{"no-primary-content":!e.hasPrimaryContent&&e.enhanceBackground}},[e.hasPrimaryContent||e.showOtherDeclarations?t("div",{class:["container",{"minimized-container":e.enableMinimized}]},[e.declListExpanded?e._e():t("div",{staticClass:"description",class:{"after-enhanced-hero":e.enhanceBackground}},[e.isRequirement?t("RequirementMetadata",{attrs:{defaultImplementationsCount:e.defaultImplementationsCount}}):e._e(),e.deprecationSummary&&e.deprecationSummary.length?t("Aside",{attrs:{kind:"deprecated"}},[t("ContentNode",{attrs:{content:e.deprecationSummary}})],1):e._e(),e.downloadNotAvailableSummary&&e.downloadNotAvailableSummary.length?t("Aside",{attrs:{kind:"note"}},[t("ContentNode",{attrs:{content:e.downloadNotAvailableSummary}})],1):e._e()],1),e.showOtherDeclarations?t("div",{staticClass:"declaration-list-menu"},[t("button",{staticClass:"declaration-list-toggle",on:{click:e.toggleDeclList}},[e._v(" "+e._s(e.declListToggleText)+" "),t("div",{staticClass:"icon"},[t("InlinePlusCircleIcon",{class:{expand:e.declListExpanded}})],1)])]):e._e(),e.primaryContentSectionsSanitized&&e.primaryContentSectionsSanitized.length?t("PrimaryContent",{class:{"with-border":!e.enhanceBackground},attrs:{conformance:e.conformance,source:e.remoteSource,sections:e.primaryContentSectionsSanitized}}):e._e(),e.shouldShowViewMoreLink?t("ViewMore",{attrs:{url:e.viewMoreLink}}):e._e()],1):e._e(),e.shouldRenderTopicSection?t("Topics",{attrs:{sections:e.topicSections,isSymbolDeprecated:e.isSymbolDeprecated,isSymbolBeta:e.isSymbolBeta,topicStyle:e.topicSectionsStyle}}):e._e(),e.defaultImplementationsSections&&!e.enableMinimized?t("DefaultImplementations",{attrs:{sections:e.defaultImplementationsSections,isSymbolDeprecated:e.isSymbolDeprecated,isSymbolBeta:e.isSymbolBeta}}):e._e(),e.relationshipsSections&&!e.enableMinimized?t("Relationships",{attrs:{sections:e.relationshipsSections}}):e._e(),e.seeAlsoSections&&!e.enableMinimized?t("SeeAlso",{attrs:{sections:e.seeAlsoSections}}):e._e()],1),e.enableOnThisPageNav?[t("OnThisPageStickyContainer",{directives:[{name:"show",rawName:"v-show",value:e.isOnThisPageNavVisible,expression:"isOnThisPageNavVisible"}]},[e.topicState.onThisPageSections.length>2?t("OnThisPageNav"):e._e()],1)]:e._e()],2),!e.isTargetIDE&&e.hasBetaContent?t("BetaLegalText"):e._e()],1),t("div",{staticClass:"visuallyhidden",attrs:{"aria-live":"polite"}},[e._v(" "+e._s(e.$t("documentation.current-page",{title:e.pageTitle}))+" ")])],1)},$=[],D=n(3078),L={class:"class",enum:"enum",protocol:"protocol",struct:"struct",uid:"uid",module:"module"},P=n(2974),O=n(2449),A=n(5947),N=n(5654),R=n(4030),B=n(7587),E=function(){var e=this,t=e._self._c;return t("div",{staticClass:"betainfo"},[t("div",{staticClass:"betainfo-container"},[t("GridRow",[t("GridColumn",{attrs:{span:{large:12}}},[t("p",{staticClass:"betainfo-label"},[e._v(e._s(e.$t("metadata.beta.software")))]),t("div",{staticClass:"betainfo-content"},[e._t("content",(function(){return[t("p",[e._v(e._s(e.$t("metadata.beta.legal")))])]}))],2),e._t("after")],2)],1)],1)])},M=[],z=n(9649),K=n(1576),Z={name:"BetaLegalText",components:{GridColumn:K.Z,GridRow:z.Z}},q=Z,j=n(1001),F=(0,j.Z)(q,E,M,!1,null,"ba3b3cc0",null),H=F.exports,V=function(){var e=this,t=e._self._c;return t("Section",{staticClass:"language",attrs:{role:"complementary","aria-label":e.$t("language")}},[t("Title",[e._v(e._s(e.$t("formats.colon",{content:e.$t("language")})))]),t("div",{staticClass:"language-list"},[t("LanguageSwitcherLink",{staticClass:"language-option swift",class:{active:e.swift.active},attrs:{url:e.swift.active?null:e.swift.url},on:{click:function(t){return e.chooseLanguage(e.swift)}}},[e._v(" "+e._s(e.swift.name)+" ")]),t("LanguageSwitcherLink",{staticClass:"language-option objc",class:{active:e.objc.active},attrs:{url:e.objc.active?null:e.objc.url},on:{click:function(t){return e.chooseLanguage(e.objc)}}},[e._v(" "+e._s(e.objc.name)+" ")])],1)],1)},W=[],U=function(){var e=this,t=e._self._c;return e.url?t("a",{attrs:{href:e.url},on:{click:function(t){return t.preventDefault(),e.$emit("click")}}},[e._t("default")],2):t("span",[e._t("default")],2)},Q=[],G={name:"LanguageSwitcherLink",props:{url:[String,Object]}},X=G,Y=(0,j.Z)(X,U,Q,!1,null,"2ca5e993",null),J=Y.exports,ee=function(){var e=this,t=e._self._c;return t("div",{staticClass:"summary-section"},[e._t("default")],2)},te=[],ne={name:"Section"},ie=ne,se=(0,j.Z)(ie,ee,te,!1,null,"3aa6f694",null),ae=se.exports,re=function(){var e=this,t=e._self._c;return t("p",{staticClass:"title"},[e._t("default")],2)},oe=[],le={name:"Title"},ce=le,de=(0,j.Z)(ce,re,oe,!1,null,"246c819c",null),ue=de.exports,he={name:"LanguageSwitcher",components:{LanguageSwitcherLink:J,Section:ae,Title:ue},inject:{isTargetIDE:{default:()=>!1},store:{default(){return{setPreferredLanguage(){}}}}},props:{interfaceLanguage:{type:String,required:!0},objcPath:{type:String,required:!0},swiftPath:{type:String,required:!0}},computed:{objc:({interfaceLanguage:e,objcPath:t,$route:{query:n}})=>({...D.Z.objectiveC,active:D.Z.objectiveC.key.api===e,url:(0,O.Q2)((0,A.Jf)(t),{...n,language:D.Z.objectiveC.key.url})}),swift:({interfaceLanguage:e,swiftPath:t,$route:{query:n}})=>({...D.Z.swift,active:D.Z.swift.key.api===e,url:(0,O.Q2)((0,A.Jf)(t),{...n,language:void 0})})},methods:{chooseLanguage(e){this.isTargetIDE||this.store.setPreferredLanguage(e.key.url),this.$router.push(e.url)}}},pe=he,ge=(0,j.Z)(pe,V,W,!1,null,"0e39c0ba",null),fe=ge.exports,me=function(){var e=this,t=e._self._c;return t("div",{staticClass:"view-more-link"},[t("router-link",{staticClass:"base-link",attrs:{to:e.url}},[e._t("default",(function(){return[e._v(e._s(e.$t("documentation.view-more")))]}))],2)],1)},ye=[],ve={name:"ViewMore",props:{url:{type:String,required:!0}}},be=ve,Te=(0,j.Z)(be,me,ye,!1,null,"3f54e653",null),_e=Te.exports,Se=function(){var e=this,t=e._self._c;return t("div",{class:["documentation-hero",{"documentation-hero--disabled":!e.enhanceBackground}],style:e.styles},[t("div",{staticClass:"icon"},[e.enhanceBackground?t("TopicTypeIcon",{key:"first",staticClass:"background-icon first-icon",attrs:{type:e.type,"image-override":e.iconOverride,"with-colors":""}}):e._e()],1),t("div",{staticClass:"documentation-hero__above-content"},[e._t("above-content")],2),t("div",{staticClass:"documentation-hero__content",class:{"short-hero":e.shortHero,"extra-bottom-padding":e.shouldShowLanguageSwitcher,"minimized-hero":e.enableMinimized}},[e._t("default")],2)])},ke=[],Ce=n(5921),we=n(5629),xe=n(1869);const Ie={red:"red",orange:"orange",yellow:"yellow",blue:"blue",green:"green",purple:"purple",gray:"gray"};var $e={name:"DocumentationHero",components:{TopicTypeIcon:Ce.Z},props:{role:{type:String,required:!0},enhanceBackground:{type:Boolean,required:!0},enableMinimized:{type:Boolean,default:!1},shortHero:{type:Boolean,required:!0},shouldShowLanguageSwitcher:{type:Boolean,required:!0},iconOverride:{type:Object,required:!1},standardColorIdentifier:{type:String,required:!1,validator:e=>Object.prototype.hasOwnProperty.call(Ie,e)}},computed:{color:({type:e})=>xe.g[we.$[e]||e]||xe.c.teal,styles:({color:e,standardColorIdentifier:t})=>({"--accent-color":`var(--color-documentation-intro-accent, var(--color-type-icon-${e}))`,"--standard-accent-color":t&&`var(--color-standard-${t}-documentation-intro-fill, var(--color-standard-${t}))`}),type:({role:e})=>{switch(e){case C.L.collection:return we.t.module;case C.L.collectionGroup:return we.t.collection;default:return e}}}},De=$e,Le=(0,j.Z)(De,Se,ke,!1,null,"283b44ff",null),Pe=Le.exports,Oe=n(352),Ae=n(3946),Ne=function(){var e=this,t=e._self._c;return t("div",{staticClass:"OnThisPageNav"},[t("ul",{staticClass:"items"},e._l(e.onThisPageSections,(function(n){return t("li",{key:n.anchor,class:e.getItemClasses(n)},[t("router-link",{staticClass:"base-link",attrs:{to:n.url},nativeOn:{click:function(t){return e.handleFocusAndScroll(n.anchor)}}},[t(e.getWrapperComponent(n),{tag:"component"},[e._v(" "+e._s(e.getTextContent(n))+" ")])],1)],1)})),0)])},Re=[];function Be(e,t){let n,i;return function(...s){const a=this;if(!i)return e.apply(a,s),void(i=Date.now());clearTimeout(n),n=setTimeout((()=>{Date.now()-i>=t&&(e.apply(a,s),i=Date.now())}),t-(Date.now()-i))}}var Ee=n(5657),Me=n(3704),ze={name:"OnThisPageNav",components:{WordBreak:Oe.Z},mixins:[Me.Z],inject:{store:{default(){return{state:{onThisPageSections:[],currentPageAnchor:null}}}}},computed:{onThisPageSections:({store:e,$route:t})=>e.state.onThisPageSections.map((e=>({...e,url:(0,O.Q2)(`#${e.anchor}`,t.query)}))),currentPageAnchor:({store:e})=>e.state.currentPageAnchor},async mounted(){window.addEventListener("scroll",this.onScroll,!1),this.$once("hook:beforeDestroy",(()=>{window.removeEventListener("scroll",this.onScroll)}))},watch:{onThisPageSections:{immediate:!0,async handler(){await(0,Ee.J)(8),this.onScroll()}}},methods:{onScroll:Be((function(){const e=this.onThisPageSections.length;if(!e)return;const{scrollY:t,innerHeight:n}=window,{scrollHeight:i}=document.body,s=t+n>=i,a=t<=0,r=.3*n+t;if(a||s){const t=a?0:e-1;return void this.store.setCurrentPageSection(this.onThisPageSections[t].anchor)}let o,l,c=null;for(o=0;ot.concat(n).concat(e),space:()=>({type:Ue.Z.InlineType.text,text:" "})}},Ge=Qe,Xe=(0,j.Z)(Ge,Ve,We,!1,null,"4c6f3ed1",null),Ye=Xe.exports,Je=function(){var e=this,t=e._self._c;return t("div",{ref:"apiChangesDiff",staticClass:"declaration-group",class:e.classes},[e.shouldCaption?t("p",{staticClass:"platforms"},[t("strong",[e._v(e._s(e.caption))])]):e._e(),e._l(e.declarationTokens,(function(n){return t("transition-expand",{key:n.identifier},[!e.hasOtherDeclarations||n.identifier===e.selectedIdentifier||e.isExpanded?t("div",{staticClass:"declaration-pill",class:{"declaration-pill--expanded":e.hasOtherDeclarations&&e.isExpanded}},[t(e.getWrapperComponent(n),{tag:"component",staticClass:"declaration-source-wrapper",on:{click:function(t){return e.selectDeclaration(n.identifier)}}},[t("Source",{class:{"selected-declaration":e.isSelectedDeclaration(n.identifier)},attrs:{tokens:n.tokens,language:e.interfaceLanguage}})],1)],1):e._e()])}))],2)},et=[],tt=function(){var e=this,t=e._self._c;return t("pre",{ref:"declarationGroup",staticClass:"source",class:{[e.multipleLinesClass]:e.displaysMultipleLines,"has-multiple-lines":e.hasMultipleLines}},[t("CodeBlock",{ref:"code"},e._l(e.formattedTokens,(function(n,i){return t("Token",e._b({key:i},"Token",e.propsFor(n),!1))})),1)],1)},nt=[];const it={instance:"-",klass:"+"};function st(e){const t=e.textContent??"";if(!t.startsWith(it.instance)&&!t.startsWith(it.klass))return;const n=e.getElementsByClassName("token-identifier");if(n.length<2)return;const i=e.textContent.indexOf(":")+1;for(let s=1;s(0,w.$8)(["theme","code","indentationWidth"],ut),formattedTokens:({language:e,formattedSwiftTokens:t,tokens:n})=>e===D.Z.swift.key.api?t:n,formattedSwiftTokens:({indentationWidth:e,tokens:t})=>{const n=" ".repeat(e);let i=!1;const s=[];let a=0,r=null,o=null,l=null,c=null,d=0,u=null;while(ae===dt.attribute||e===dt.externalParam;e.text&&e.text.endsWith(", ")&&g&&f(g)&&(h.text=`${e.text.trimEnd()}\n${n}`,i=!0),s.push(h),a+=1}if(i&&null!==r){const e=s[r].text;s[r].text=`${e}\n${n}`}if(i&&null!==l){const e=s[l].text,t=e.slice(0,c),n=e.slice(c),i=`${t}\n${n}`;s[l].text=i}return s},hasMultipleLines({formattedTokens:e}){return e.reduce(((t,n,i)=>{let s=/\n/g;return i===e.length-1&&(s=/\n(?!$)/g),n.text?t+(n.text.match(s)||[]).length:t}),1)>=2}},methods:{propsFor(e){return{kind:e.kind,identifier:e.identifier,text:e.text,tokens:e.tokens}},handleWindowResize(){this.displaysMultipleLines=(0,rt.s)(this.$refs.declarationGroup)}},async mounted(){window.addEventListener("resize",this.handleWindowResize),this.language===D.Z.objectiveC.key.api&&(await this.$nextTick(),at(this.$refs.code.$el,this.language)),this.handleWindowResize()},beforeDestroy(){window.removeEventListener("resize",this.handleWindowResize)}},pt=ht,gt=(0,j.Z)(pt,tt,nt,!1,null,"d22a3f50",null),ft=gt.exports,mt=n(9732),yt=n(1842),vt={name:"DeclarationGroup",components:{Source:ft,TransitionExpand:mt.Z},data(){return{selectedIdentifier:this.identifier}},mixins:[yt.PH],inject:{languages:{default:()=>new Set},interfaceLanguage:{default:()=>D.Z.swift.key.api},symbolKind:{default:()=>{}},store:{default:()=>({state:{references:{}}})},identifier:{default:()=>{}}},props:{declaration:{type:Object,required:!0},shouldCaption:{type:Boolean,default:!1},changeType:{type:String,required:!1},declListExpanded:{type:Boolean,default:!1}},computed:{hasOtherDeclarations:({declaration:e})=>e.otherDeclarations||null,declarationTokens:({declaration:e,hasOtherDeclarations:t,identifier:n})=>{if(!t)return[e];const{otherDeclarations:{declarations:i,displayIndex:s},tokens:a}=e;return[...i.slice(0,s),{tokens:a,identifier:n},...i.slice(s)]},classes:({changeType:e,multipleLinesClass:t,displaysMultipleLinesAfterAPIChanges:n})=>({[`declaration-group--changed declaration-group--${e}`]:e,[t]:n}),caption(){return this.declaration.platforms.join(", ")},isSwift:({interfaceLanguage:e})=>e===D.Z.swift.key.api,references:({store:e})=>e.state.references,isExpanded:{get:({declListExpanded:e})=>e,set(e){this.$emit("update:declListExpanded",e)}}},methods:{async selectDeclaration(e){if(e===this.identifier)return;this.selectedIdentifier=e,await this.$nextTick(),this.isExpanded=!1,await(0,Ee.X)(500);const t=(0,O.Q2)(this.references[e].url,this.$route.query);this.$router.push(t)},getWrapperComponent(e){return this.isExpanded&&e.identifier!==this.identifier?"button":"div"},isSelectedDeclaration(e){return e===this.selectedIdentifier}}},bt=vt,Tt=(0,j.Z)(bt,Je,et,!1,null,"475cd275",null),_t=Tt.exports,St=function(){var e=this,t=e._self._c;return t("div",{staticClass:"declaration-diff"},[t("div",{staticClass:"declaration-diff-current"},[t("div",{staticClass:"declaration-diff-version"},[e._v("Current")]),e._l(e.currentDeclarations,(function(n,i){return t("DeclarationList",{key:i,attrs:{declaration:n,"should-caption":e.currentDeclarations.length>1,changeType:e.changeType}})}))],2),t("div",{staticClass:"declaration-diff-previous"},[t("div",{staticClass:"declaration-diff-version"},[e._v("Previous")]),e._l(e.previousDeclarations,(function(n,i){return t("DeclarationList",{key:i,attrs:{declaration:n,"should-caption":e.previousDeclarations.length>1,changeType:e.changeType}})}))],2)])},kt=[],Ct={name:"DeclarationDiff",components:{DeclarationList:_t},props:{changes:{type:Object,required:!0},changeType:{type:String,required:!0}},computed:{previousDeclarations:({changes:e})=>e.declaration.previous||[],currentDeclarations:({changes:e})=>e.declaration.new||[]}},wt=Ct,xt=(0,j.Z)(wt,St,kt,!1,null,"b3e98f40",null),It=xt.exports,$t=function(){var e=this,t=e._self._c;return t("a",{staticClass:"declaration-source-link",attrs:{href:e.url,title:`Open source file for ${e.fileName}`,target:"_blank"}},[e.isSwiftFile?t("SwiftFileIcon",{staticClass:"declaration-icon"}):e._e(),t("WordBreak",[e._v(e._s(e.fileName))])],1)},Dt=[],Lt=n(7834),Pt={name:"DeclarationSourceLink",components:{WordBreak:Oe.Z,SwiftFileIcon:Lt.Z},props:{url:{type:String,required:!0},fileName:{type:String,required:!0}},computed:{isSwiftFile:({fileName:e})=>e.endsWith(".swift")}},Ot=Pt,At=(0,j.Z)(Ot,$t,Dt,!1,null,"5863919c",null),Nt=At.exports,Rt=n(9426),Bt={name:"Declaration",components:{DeclarationDiff:It,DeclarationList:_t,DeclarationSourceLink:Nt,ConditionalConstraints:Ye},constants:{ChangeTypes:Rt.yf,multipleLinesClass:ot._},inject:["identifier","store"],data:({store:{state:e}})=>({state:e,multipleLinesClass:ot._}),props:{conformance:{type:Object,required:!1},source:{type:Object,required:!1},declarations:{type:Array,required:!0},declListExpanded:{type:Boolean,default:!1}},computed:{hasPlatformVariants(){return this.declarations.length>1},hasModifiedChanges({declarationChanges:e}){if(!e||!e.declaration)return!1;const t=e.declaration;return!(!(t.new||[]).length||!(t.previous||[]).length)},declarationChanges:({state:{apiChanges:e},identifier:t})=>e&&e[t],changeType:({declarationChanges:e,hasModifiedChanges:t})=>{if(!e)return;const n=e.declaration;return n?t?Rt.yf.modified:e.change:e.change===Rt.yf.added?Rt.yf.added:void 0},changeClasses:({changeType:e})=>({[`changed changed-${e}`]:e}),isExpanded:{get:({declListExpanded:e})=>e,set(e){this.$emit("update:declListExpanded",e)}}}},Et=Bt,Mt=(0,j.Z)(Et,Fe,He,!1,null,"01201553",null),zt=Mt.exports,Kt=n(6772),Zt=function(){var e=this,t=e._self._c;return t("ContentNode",e._b({staticClass:"abstract"},"ContentNode",e.$props,!1))},qt=[],jt={name:"Abstract",components:{ContentNode:Ue.Z},props:Ue.Z.props},Ft=jt,Ht=(0,j.Z)(Ft,Zt,qt,!1,null,"f3f57cbe",null),Vt=Ht.exports,Wt=n(7605),Ut=function(){var e=this,t=e._self._c;return t("TopicsTable",{attrs:{anchor:e.contentSectionData.anchor,title:e.$t(e.contentSectionData.title),isSymbolDeprecated:e.isSymbolDeprecated,isSymbolBeta:e.isSymbolBeta,sections:e.sections,wrapTitle:!0}})},Qt=[];const Gt={topics:{title:"sections.topics",anchor:"topics",level:2},defaultImplementations:{title:"sections.default-implementations",anchor:"default-implementations",level:2},relationships:{title:"sections.relationships",anchor:"relationships",level:2},seeAlso:{title:"sections.see-also",anchor:"see-also",level:2}},Xt={[je.attributes]:{title:"sections.attributes",anchor:"attributes",level:2},[je.details]:{title:"sections.details",anchor:"details",level:2},[je.parameters]:{title:"sections.parameters",anchor:"parameters",level:2},[je.possibleValues]:{title:"sections.possible-values",anchor:"possibleValues",level:2}};var Yt=function(){var e=this,t=e._self._c;return t("ContentTable",{attrs:{anchor:e.anchor,title:e.title}},e._l(e.sectionsWithTopics,(function(n,i){return t("ContentTableSection",{key:`${n.title}_${i}`,class:{"no-title":!n.title},attrs:{title:n.title,anchor:n.anchor},scopedSlots:e._u([n.title&&e.wrapTitle?{key:"title",fn:function({className:i}){return[t("LinkableHeading",{class:i,attrs:{level:3,anchor:n.anchor}},[t("WordBreak",[e._v(e._s(n.title))])],1)]}}:null,n.abstract?{key:"abstract",fn:function(){return[t("ContentNode",{attrs:{content:n.abstract}})]},proxy:!0}:null,n.discussion?{key:"discussion",fn:function(){return[t("ContentNode",{attrs:{content:n.discussion.content}})]},proxy:!0}:null],null,!0)},[e.shouldRenderList?e._l(n.topics,(function(n){return t("TopicsLinkBlock",{key:n.identifier,staticClass:"topic",attrs:{topic:n,isSymbolDeprecated:e.isSymbolDeprecated,isSymbolBeta:e.isSymbolBeta}})})):t("TopicsLinkCardGrid",{staticClass:"topic",attrs:{items:n.topics,topicStyle:e.topicStyle}})],2)})),1)},Jt=[],en=n(1105),tn=n(8039),nn=n(5953),sn=function(){var e=this,t=e._self._c;return t("section",{staticClass:"contenttable alt-light"},[t("div",{staticClass:"container"},[t("LinkableHeading",{staticClass:"title",attrs:{anchor:e.anchor}},[e._v(e._s(e.title))]),e._t("default")],2)])},an=[],rn={name:"ContentTable",components:{LinkableHeading:tn.Z},props:{anchor:{type:String,required:!0},title:{type:String,required:!0}}},on=rn,ln=(0,j.Z)(on,sn,an,!1,null,"6e075935",null),cn=ln.exports,dn=function(){var e=this,t=e._self._c;return t("div",{staticClass:"contenttable-section"},[t("div",{staticClass:"section-title"},[e._t("title",(function(){return[e.title?t("LinkableHeading",{class:e.className,attrs:{level:3,anchor:e.anchorComputed}},[e._v(e._s(e.title))]):e._e()]}),{className:e.className})],2),t("div",{staticClass:"section-content"},[e._t("abstract"),e._t("discussion"),e._t("default")],2)])},un=[],hn=n(3208);const pn="contenttable-title";var gn={name:"ContentTableSection",components:{LinkableHeading:tn.Z},props:{title:{type:String,required:!1},anchor:{type:String,default:null}},computed:{anchorComputed:({title:e,anchor:t})=>t||(0,hn.HA)(e||""),className:()=>pn}},fn=gn,mn=(0,j.Z)(fn,dn,un,!1,null,"1b0546d9",null),yn=mn.exports,vn=n(8104),bn={name:"TopicsTable",mixins:[nn.Z],components:{TopicsLinkCardGrid:en.Z,WordBreak:Oe.Z,ContentTable:cn,TopicsLinkBlock:vn["default"],ContentNode:Ue.Z,ContentTableSection:yn,LinkableHeading:tn.Z},props:{isSymbolDeprecated:Boolean,isSymbolBeta:Boolean,sections:{type:Array,required:!0},title:{type:String,required:!1,default(){return"Topics"}},anchor:{type:String,required:!1,default(){return"topics"}},wrapTitle:{type:Boolean,default:!1},topicStyle:{type:String,default:Ae.o.list}},computed:{shouldRenderList:({topicStyle:e})=>e===Ae.o.list,sectionsWithTopics(){return this.sections.map((e=>({...e,topics:e.identifiers.reduce(((e,t)=>this.references[t]?e.concat(this.references[t]):e),[])})))}}},Tn=bn,_n=(0,j.Z)(Tn,Yt,Jt,!1,null,"1c2724f5",null),Sn=_n.exports,kn={name:"DefaultImplementations",components:{TopicsTable:Sn},computed:{contentSectionData:()=>Gt.defaultImplementations},props:{isSymbolDeprecated:Boolean,isSymbolBeta:Boolean,sections:Sn.props.sections}},Cn=kn,wn=(0,j.Z)(Cn,Ut,Qt,!1,null,null,null),xn=wn.exports,In=function(){var e=this,t=e._self._c;return t("div",{staticClass:"primary-content"},e._l(e.sections,(function(n,i){return t(e.componentFor(n),e._b({key:i,tag:"component"},"component",e.propsFor(n),!1))})),1)},$n=[],Dn=function(){var e=this,t=e._self._c;return t("section",{staticClass:"attributes"},[t("LinkableHeading",{attrs:{anchor:e.section.anchor,level:e.section.level}},[e._v(" "+e._s(e.$t(e.section.title))+" ")]),t("ParameterAttributes",{attrs:{attributes:e.attributes}})],1)},Ln=[],Pn=function(){var e=this,t=e._self._c;return t("div",{staticClass:"parameter-attributes"},[e.shouldRender(e.AttributeKind.default)?t("ParameterMetaAttribute",e._b({scopedSlots:e._u([{key:"default",fn:function({attribute:n}){return[e._v(" "+e._s(e.$t("formats.colon",{content:n.title||e.$t("parameters.default")}))),t("code",[e._v(e._s(n.value))])]}}],null,!1,2998238055)},"ParameterMetaAttribute",{kind:e.AttributeKind.default,attributes:e.attributesObject,changes:e.changes},!1)):e._e(),e.shouldRender(e.AttributeKind.minimum)?t("ParameterMetaAttribute",e._b({scopedSlots:e._u([{key:"default",fn:function({attribute:n}){return[e._v(" "+e._s(e.$t("formats.colon",{content:n.title||e.$t("parameters.minimum")}))),t("code",[e._v(e._s(n.value))])]}}],null,!1,859757818)},"ParameterMetaAttribute",{kind:e.AttributeKind.minimum,attributes:e.attributesObject,changes:e.changes},!1)):e._e(),e.shouldRender(e.AttributeKind.minimumExclusive)?t("ParameterMetaAttribute",e._b({scopedSlots:e._u([{key:"default",fn:function({attribute:n}){return[e._v(" "+e._s(e.$t("formats.colon",{content:n.title||e.$t("parameters.minimum")}))),t("code",[e._v("> "+e._s(n.value))])]}}],null,!1,770347247)},"ParameterMetaAttribute",{kind:e.AttributeKind.minimumExclusive,attributes:e.attributesObject,changes:e.changes},!1)):e._e(),e.shouldRender(e.AttributeKind.maximum)?t("ParameterMetaAttribute",e._b({scopedSlots:e._u([{key:"default",fn:function({attribute:n}){return[e._v(" "+e._s(e.$t("formats.colon",{content:n.title||e.$t("parameters.maximum")}))),t("code",[e._v(e._s(n.value))])]}}],null,!1,1190666532)},"ParameterMetaAttribute",{kind:e.AttributeKind.maximum,attributes:e.attributesObject,changes:e.changes},!1)):e._e(),e.shouldRender(e.AttributeKind.maximumExclusive)?t("ParameterMetaAttribute",e._b({scopedSlots:e._u([{key:"default",fn:function({attribute:n}){return[e._v(" "+e._s(e.$t("formats.colon",{content:n.title||e.$t("parameters.maximum")}))),t("code",[e._v("< "+e._s(n.value))])]}}],null,!1,1156490099)},"ParameterMetaAttribute",{kind:e.AttributeKind.maximumExclusive,attributes:e.attributesObject,changes:e.changes},!1)):e._e(),e.shouldRender(e.AttributeKind.allowedTypes)?t("ParameterMetaAttribute",e._b({scopedSlots:e._u([{key:"default",fn:function({attribute:n}){return[e._v(" "+e._s(e.$t("formats.colon",{content:e.$tc("parameters.possible-types",e.fallbackToValues(n).length)}))),t("code",[e._l(e.fallbackToValues(n),(function(i,s){return[e._l(i,(function(i,a){return[t("DeclarationToken",e._b({key:`${s}-${a}`},"DeclarationToken",i,!1)),s+1({new:null,previous:null})},value:{type:[Object,Array,String,Boolean],default:null},wrapChanges:{type:Boolean,default:!0},renderSingleChange:{type:Boolean,default:!1}},render(e){const{value:t,changes:n={},wrapChanges:i,renderSingleChange:s}=this,{new:a,previous:r}=n,o=(t,n)=>{const s=this.$scopedSlots.default({value:t});return n&&i?e("div",{class:n},[s]):s?s[0]:null};if(a||r){const t=o(a,Rn.added),n=o(r,Rn.removed);return s?a&&!r?t:n:e("div",{class:"property-changegroup"},[a?t:"",r?n:""])}return o(t)}},zn=Mn,Kn=(0,j.Z)(zn,Bn,En,!1,null,null,null),Zn=Kn.exports,qn={name:"ParameterMetaAttribute",components:{RenderChanged:Zn},props:{kind:{type:String,required:!0},attributes:{type:Object,required:!0},changes:{type:Object,default:()=>({})}}},jn=qn,Fn=(0,j.Z)(jn,An,Nn,!1,null,"f911f232",null),Hn=Fn.exports;const Vn={allowedTypes:"allowedTypes",allowedValues:"allowedValues",default:"default",maximum:"maximum",maximumExclusive:"maximumExclusive",minimum:"minimum",minimumExclusive:"minimumExclusive"};var Wn={name:"ParameterAttributes",components:{ParameterMetaAttribute:Hn,DeclarationToken:ct["default"]},constants:{AttributeKind:Vn},props:{attributes:{type:Array,default:()=>[]},changes:{type:Object,default:()=>({})}},computed:{AttributeKind:()=>Vn,attributesObject:({attributes:e})=>e.reduce(((e,t)=>({...e,[t.kind]:t})),{})},methods:{shouldRender(e){return Object.prototype.hasOwnProperty.call(this.attributesObject,e)},fallbackToValues:e=>{const t=e||[];return Array.isArray(t)?t:t.values}}},Un=Wn,Qn=(0,j.Z)(Un,Pn,On,!1,null,null,null),Gn=Qn.exports,Xn={name:"Attributes",components:{LinkableHeading:tn.Z,ParameterAttributes:Gn},props:{attributes:{type:Array,required:!0}},computed:{section:({sectionKind:e})=>Xt[e],sectionKind:()=>je.attributes}},Yn=Xn,Jn=(0,j.Z)(Yn,Dn,Ln,!1,null,"c0edcb84",null),ei=Jn.exports,ti=function(){var e=this,t=e._self._c;return t("section",[t("LinkableHeading",{attrs:{anchor:e.contentSectionData.anchor}},[e._v(" "+e._s(e.$t(e.contentSectionData.title))+" ")]),t("dl",{staticClass:"datalist"},[e._l(e.values,(function(n){return[t("dt",{key:`${n.name}:name`,staticClass:"param-name"},[t("WordBreak",{attrs:{tag:"code"}},[e._v(e._s(n.name))])],1),n.content?t("dd",{key:`${n.name}:content`,staticClass:"value-content"},[t("ContentNode",{attrs:{content:n.content}})],1):e._e()]}))],2)],1)},ni=[],ii=n(9519),si={name:"PossibleValues",components:{ContentNode:ii["default"],LinkableHeading:tn.Z,WordBreak:Oe.Z},props:{values:{type:Array,required:!0}},computed:{contentSectionData:()=>Xt[je.possibleValues]}},ai=si,ri=(0,j.Z)(ai,ti,ni,!1,null,null,null),oi=ri.exports,li=function(){var e=this,t=e._self._c;return t("section",[t("LinkableHeading",{attrs:{anchor:e.anchor}},[e._v(e._s(e.title))]),t("DeclarationSource",{attrs:{tokens:e.tokens}})],1)},ci=[],di={name:"RestEndpoint",components:{DeclarationSource:ft,LinkableHeading:tn.Z},props:{title:{type:String,required:!0},tokens:{type:Array,required:!0}},computed:{anchor:({title:e})=>(0,hn.HA)(e)}},ui=di,hi=(0,j.Z)(ui,li,ci,!1,null,null,null),pi=hi.exports,gi=function(){var e=this,t=e._self._c;return t("section",{staticClass:"details"},[t("LinkableHeading",{attrs:{anchor:e.contentSectionData.anchor}},[e._v(" "+e._s(e.$t(e.contentSectionData.title))+" ")]),t("dl",[e.isSymbol?[t("dt",{key:`${e.details.name}:name`,staticClass:"detail-type"},[e._v(" "+e._s(e.$t("metadata.details.name"))+" ")]),t("dd",{key:`${e.details.ideTitle}:content`,staticClass:"detail-content"},[e._v(" "+e._s(e.details.ideTitle)+" ")])]:e._e(),e.isTitle?[t("dt",{key:`${e.details.name}:key`,staticClass:"detail-type"},[e._v(" "+e._s(e.$t("metadata.details.key"))+" ")]),t("dd",{key:`${e.details.ideTitle}:content`,staticClass:"detail-content"},[e._v(" "+e._s(e.details.name)+" ")])]:e._e(),t("dt",{key:`${e.details.name}:type`,staticClass:"detail-type"},[e._v(" "+e._s(e.$t("metadata.details.type"))+" ")]),t("dd",{staticClass:"detail-content"},[t("PropertyListKeyType",{attrs:{types:e.details.value}})],1)],2)],1)},fi=[],mi=function(){var e=this,t=e._self._c;return t("div",{staticClass:"type"},[e._v(e._s(e.typeOutput))])},yi=[],vi={name:"PropertyListKeyType",props:{types:{type:Array,required:!0}},computed:{englishTypes(){return this.types.map((({arrayMode:e,baseType:t="*"})=>e?`array of ${this.pluralizeKeyType(t)}`:t))},typeOutput(){return this.englishTypes.length>2?[this.englishTypes.slice(0,this.englishTypes.length-1).join(", "),this.englishTypes[this.englishTypes.length-1]].join(", or "):this.englishTypes.join(" or ")}},methods:{pluralizeKeyType(e){switch(e){case"dictionary":return"dictionaries";case"array":case"number":case"string":return`${e}s`;default:return e}}}},bi=vi,Ti=(0,j.Z)(bi,mi,yi,!1,null,"791bac44",null),_i=Ti.exports,Si={name:"PropertyListKeyDetails",components:{PropertyListKeyType:_i,LinkableHeading:tn.Z},props:{details:{type:Object,required:!0}},computed:{contentSectionData:()=>Xt[je.details],isTitle(){return"title"===this.details.titleStyle&&this.details.ideTitle},isSymbol(){return"symbol"===this.details.titleStyle&&this.details.ideTitle}}},ki=Si,Ci=(0,j.Z)(ki,gi,fi,!1,null,"d66cd00c",null),wi=Ci.exports,xi=function(){var e=this,t=e._self._c;return t("section",{staticClass:"parameters"},[t("LinkableHeading",{attrs:{anchor:e.contentSectionData.anchor}},[e._v(" "+e._s(e.$t(e.contentSectionData.title))+" ")]),t("dl",[e._l(e.parameters,(function(n){return[t("dt",{key:`${n.name}:name`,staticClass:"param-name"},[t("code",[e._v(e._s(n.name))])]),t("dd",{key:`${n.name}:content`,staticClass:"param-content"},[t("ContentNode",{attrs:{content:n.content}})],1)]}))],2)],1)},Ii=[],$i={name:"Parameters",components:{ContentNode:Ue.Z,LinkableHeading:tn.Z},props:{parameters:{type:Array,required:!0}},computed:{contentSectionData:()=>Xt[je.parameters]}},Di=$i,Li=(0,j.Z)(Di,xi,Ii,!1,null,"5ef1227e",null),Pi=Li.exports,Oi=function(){var e=this,t=e._self._c;return t("section",[t("LinkableHeading",{attrs:{anchor:e.anchor}},[e._v(e._s(e.title))]),t("ParametersTable",{staticClass:"property-table",attrs:{parameters:e.properties,changes:e.propertyChanges},scopedSlots:e._u([{key:"symbol",fn:function({name:n,type:i,content:s,changes:a,deprecated:r}){return[t("div",{staticClass:"property-name",class:{deprecated:r}},[t("WordBreak",{attrs:{tag:"code"}},[e._v(e._s(n))])],1),e.shouldShiftType({name:n,content:s})?e._e():t("PossiblyChangedType",{attrs:{type:i,changes:a.type}})]}},{key:"description",fn:function({name:n,type:i,attributes:s,content:a,required:r,changes:o,deprecated:l,readOnly:c}){return[e.shouldShiftType({name:n,content:a})?t("PossiblyChangedType",{attrs:{type:i,changes:o.type}}):e._e(),l?[t("Badge",{staticClass:"property-deprecated",attrs:{variant:"deprecated"}}),e._v("  ")]:e._e(),t("PossiblyChangedTextAttribute",{attrs:{changes:o.required,value:r}},[e._v(" "+e._s(e.$t("formats.parenthesis",{content:e.$t("required")}))+" ")]),t("PossiblyChangedTextAttribute",{attrs:{changes:o.readOnly,value:c}},[e._v(" "+e._s(e.$t("formats.parenthesis",{content:e.$t("read-only")}))+" ")]),a?t("ContentNode",{attrs:{content:a}}):e._e(),t("ParameterAttributes",{attrs:{attributes:s,changes:o.attributes}})]}}])})],1)},Ai=[],Ni={inject:["identifier","store"],data:({store:{state:e}})=>({state:e}),computed:{apiChanges:({state:{apiChanges:e},identifier:t})=>e&&e[t]}},Ri=n(6137),Bi=function(){var e=this,t=e._self._c;return t("div",{staticClass:"parameters-table"},e._l(e.parameters,(function(n){return t("Row",{key:n[e.keyBy],staticClass:"param",class:e.changedClasses(n[e.keyBy])},[t("Column",{staticClass:"param-symbol",attrs:{span:{large:3,small:12}}},[e._t("symbol",null,null,e.getProps(n,e.changes[n[e.keyBy]]))],2),t("Column",{staticClass:"param-content",attrs:{span:{large:9,small:12}}},[e._t("description",null,null,e.getProps(n,e.changes[n[e.keyBy]]))],2)],1)})),1)},Ei=[],Mi={name:"ParametersTable",components:{Row:z.Z,Column:K.Z},props:{parameters:{type:Array,required:!0},changes:{type:Object,default:()=>({})},keyBy:{type:String,default:"name"}},methods:{getProps(e,t={}){return{...e,changes:t}},changedClasses(e){const{changes:t}=this,{change:n}=t[e]||{};return{[`changed changed-${n}`]:n}}}},zi=Mi,Ki=(0,j.Z)(zi,Bi,Ei,!1,null,"eee7e94e",null),Zi=Ki.exports,qi=function(){var e=this,t=e._self._c;return t("RenderChanged",{attrs:{renderSingleChange:"",value:e.value,changes:e.changes},scopedSlots:e._u([{key:"default",fn:function({value:n}){return[n?t("span",{staticClass:"property-text"},[e._t("default")],2):e._e()]}}],null,!0)})},ji=[],Fi={name:"PossiblyChangedTextAttribute",components:{RenderChanged:Zn},props:{changes:{type:Object,required:!1},value:{type:Boolean,default:!1}}},Hi=Fi,Vi=(0,j.Z)(Hi,qi,ji,!1,null,null,null),Wi=Vi.exports,Ui=function(){var e=this,t=e._self._c;return t("RenderChanged",{attrs:{value:e.type,wrapChanges:!1,changes:e.changes},scopedSlots:e._u([{key:"default",fn:function({value:n}){return[t("DeclarationTokenGroup",{staticClass:"property-metadata property-type",attrs:{type:e.getValues(n)}})]}}])})},Qi=[],Gi=function(){var e=this,t=e._self._c;return e.type&&e.type.length?t("div",[t("code",e._l(e.type,(function(n,i){return t("DeclarationToken",e._b({key:i},"DeclarationToken",n,!1))})),1)]):e._e()},Xi=[],Yi={name:"DeclarationTokenGroup",components:{DeclarationToken:ct["default"]},props:{type:{type:Array,default:()=>[],required:!1}}},Ji=Yi,es=(0,j.Z)(Ji,Gi,Xi,!1,null,null,null),ts=es.exports,ns={name:"PossiblyChangedType",components:{DeclarationTokenGroup:ts,RenderChanged:Zn},props:{type:{type:Array,required:!0},changes:{type:Object,required:!1}},methods:{getValues(e){return Array.isArray(e)?e:e.values}}},is=ns,ss=(0,j.Z)(is,Ui,Qi,!1,null,"549ed0a8",null),as=ss.exports,rs={name:"PropertyTable",mixins:[Ni],components:{Badge:Ri.Z,WordBreak:Oe.Z,PossiblyChangedTextAttribute:Wi,PossiblyChangedType:as,ParameterAttributes:Gn,ContentNode:Ue.Z,ParametersTable:Zi,LinkableHeading:tn.Z},props:{title:{type:String,required:!0},properties:{type:Array,required:!0}},computed:{anchor:({title:e})=>(0,hn.HA)(e),propertyChanges:({apiChanges:e})=>(e||{}).properties},methods:{shouldShiftType:({content:e=[],name:t})=>!e.length&&t}},os=rs,ls=(0,j.Z)(os,Oi,Ai,!1,null,"39899ccf",null),cs=ls.exports,ds=function(){var e=this,t=e._self._c;return t("section",[t("LinkableHeading",{attrs:{anchor:e.anchor}},[e._v(e._s(e.title))]),t("ParametersTable",{attrs:{parameters:[e.bodyParam],changes:e.bodyChanges,keyBy:"key"},scopedSlots:e._u([{key:"symbol",fn:function({type:n,content:i,changes:s,name:a}){return[e.shouldShiftType({name:a,content:i})?e._e():t("PossiblyChangedType",{attrs:{type:n,changes:s.type}})]}},{key:"description",fn:function({name:n,content:i,mimeType:s,type:a,changes:r}){return[e.shouldShiftType({name:n,content:i})?t("PossiblyChangedType",{attrs:{type:a,changes:r.type}}):e._e(),i?t("ContentNode",{attrs:{content:i}}):e._e(),s?t("PossiblyChangedMimetype",{attrs:{mimetype:s,changes:r.mimetype,change:r.change}}):e._e()]}}])}),e.parts.length?[t("h3",[e._v(e._s(e.$t("sections.parts")))]),t("ParametersTable",{staticClass:"parts",attrs:{parameters:e.parts,changes:e.partsChanges},scopedSlots:e._u([{key:"symbol",fn:function({name:n,type:i,content:s,changes:a}){return[t("div",{staticClass:"part-name"},[t("WordBreak",{attrs:{tag:"code"}},[e._v(e._s(n))])],1),s?t("PossiblyChangedType",{attrs:{type:i,changes:a.type}}):e._e()]}},{key:"description",fn:function({content:n,mimeType:i,required:s,type:a,attributes:r,changes:o,readOnly:l}){return[t("div",[n?e._e():t("PossiblyChangedType",{attrs:{type:a,changes:o.type}}),t("PossiblyChangedTextAttribute",{attrs:{changes:o.required,value:s}},[e._v("(Required) ")]),t("PossiblyChangedTextAttribute",{attrs:{changes:o.readOnly,value:l}},[e._v("(Read only) ")]),n?t("ContentNode",{attrs:{content:n}}):e._e(),i?t("PossiblyChangedMimetype",{attrs:{mimetype:i,changes:o.mimetype,change:o.change}}):e._e(),t("ParameterAttributes",{attrs:{attributes:r,changes:o.attributes}})],1)]}}],null,!1,1779956822)})]:e._e()],2)},us=[],hs=function(){var e=this,t=e._self._c;return t("RenderChanged",{attrs:{changes:e.changeValues,value:e.mimetype},scopedSlots:e._u([{key:"default",fn:function({value:n}){return[t("div",{staticClass:"response-mimetype"},[e._v(" "+e._s(e.$t("content-type",{value:n}))+" ")])]}}])})},ps=[],gs={name:"PossiblyChangedMimetype",components:{RenderChanged:Zn},props:{mimetype:{type:String,required:!0},changes:{type:[Object,String],required:!1},change:{type:String,required:!1}},computed:{changeValues({change:e,changes:t}){return e===Rt.yf.modified&&"string"!==typeof t?t:void 0}}},fs=gs,ms=(0,j.Z)(fs,hs,ps,!1,null,"18890a0f",null),ys=ms.exports;const vs="restRequestBody";var bs={name:"RestBody",mixins:[Ni],components:{PossiblyChangedMimetype:ys,PossiblyChangedTextAttribute:Wi,PossiblyChangedType:as,WordBreak:Oe.Z,ParameterAttributes:Gn,ContentNode:Ue.Z,ParametersTable:Zi,LinkableHeading:tn.Z},constants:{ChangesKey:vs},props:{bodyContentType:{type:Array,required:!0},content:{type:Array},mimeType:{type:String,required:!0},parts:{type:Array,default:()=>[]},title:{type:String,required:!0}},computed:{anchor:({title:e})=>(0,hn.HA)(e),bodyParam:({bodyContentType:e,content:t,mimeType:n})=>({key:vs,content:t,mimeType:n,type:e}),bodyChanges:({apiChanges:e})=>e||{},partsChanges:({bodyChanges:e})=>(e[vs]||{}).parts},methods:{shouldShiftType:({content:e=[],name:t})=>!e.length&&t}},Ts=bs,_s=(0,j.Z)(Ts,ds,us,!1,null,"68facc94",null),Ss=_s.exports,ks=function(){var e=this,t=e._self._c;return t("section",[t("LinkableHeading",{attrs:{anchor:e.anchor}},[e._v(e._s(e.title))]),t("ParametersTable",{attrs:{parameters:e.parameters,changes:e.parameterChanges},scopedSlots:e._u([{key:"symbol",fn:function({name:n,type:i,content:s,changes:a,deprecated:r}){return[t("div",{staticClass:"param-name",class:{deprecated:r}},[t("WordBreak",{attrs:{tag:"code"}},[e._v(e._s(n))])],1),e.shouldShiftType({content:s,name:n})?e._e():t("PossiblyChangedType",{attrs:{type:i,changes:a.type}})]}},{key:"description",fn:function({name:n,type:i,content:s,required:a,attributes:r,changes:o,deprecated:l,readOnly:c}){return[t("div",[e.shouldShiftType({content:s,name:n})?t("PossiblyChangedType",{attrs:{type:i,changes:o.type}}):e._e(),l?[t("Badge",{staticClass:"param-deprecated",attrs:{variant:"deprecated"}}),e._v("  ")]:e._e(),t("PossiblyChangedTextAttribute",{attrs:{changes:o.required,value:a}},[e._v(" "+e._s(e.$t("formats.parenthesis",{content:e.$t("required")}))+" ")]),t("PossiblyChangedTextAttribute",{attrs:{changes:o.readOnly,value:c}},[e._v(" "+e._s(e.$t("formats.parenthesis",{content:e.$t("read-only")}))+" ")]),s?t("ContentNode",{attrs:{content:s}}):e._e(),t("ParameterAttributes",{attrs:{attributes:r,changes:o}})],2)]}}])})],1)},Cs=[],ws={name:"RestParameters",mixins:[Ni],components:{Badge:Ri.Z,PossiblyChangedType:as,PossiblyChangedTextAttribute:Wi,ParameterAttributes:Gn,WordBreak:Oe.Z,ContentNode:Ue.Z,ParametersTable:Zi,LinkableHeading:tn.Z},props:{title:{type:String,required:!0},parameters:{type:Array,required:!0}},computed:{anchor:({title:e})=>(0,hn.HA)(e),parameterChanges:({apiChanges:e})=>(e||{}).restParameters},methods:{shouldShiftType:({content:e=[],name:t})=>!e.length&&t}},xs=ws,Is=(0,j.Z)(xs,ks,Cs,!1,null,"0d9b752e",null),$s=Is.exports,Ds=function(){var e=this,t=e._self._c;return t("section",[t("LinkableHeading",{attrs:{anchor:e.anchor}},[e._v(e._s(e.title))]),t("ParametersTable",{attrs:{parameters:e.sanitizedResponses,changes:e.propertyChanges,"key-by":"status"},scopedSlots:e._u([{key:"symbol",fn:function({status:n,type:i,reason:s,content:a,changes:r}){return[t("div",{staticClass:"response-name"},[t("code",[e._v(" "+e._s(n)+" "),t("span",{staticClass:"reason"},[e._v(e._s(s))])])]),e.shouldShiftType({content:a,reason:s,status:n})?e._e():t("PossiblyChangedType",{attrs:{type:i,changes:r.type}})]}},{key:"description",fn:function({content:n,mimeType:i,reason:s,type:a,status:r,changes:o}){return[e.shouldShiftType({content:n,reason:s,status:r})?t("PossiblyChangedType",{attrs:{type:a,changes:o.type}}):e._e(),t("div",{staticClass:"response-reason"},[t("code",[e._v(e._s(s))])]),n?t("ContentNode",{attrs:{content:n}}):e._e(),i?t("PossiblyChangedMimetype",{attrs:{mimetype:i,changes:o.mimetype,change:o.change}}):e._e()]}}])})],1)},Ls=[],Ps={name:"RestResponses",mixins:[Ni],components:{PossiblyChangedMimetype:ys,PossiblyChangedType:as,ContentNode:Ue.Z,ParametersTable:Zi,LinkableHeading:tn.Z},props:{title:{type:String,required:!0},responses:{type:Array,required:!0}},computed:{anchor:({title:e})=>(0,hn.HA)(e),propertyChanges:({apiChanges:e})=>(e||{}).restResponses,sanitizedResponses:({responses:e,sanitizeResponse:t})=>e.map(t)},methods:{sanitizeResponse:({mimetype:e,...t})=>e?{...t,mimeType:e}:t,shouldShiftType:({content:e=[],reason:t,status:n})=>!(e.length||t)&&n}},Os=Ps,As=(0,j.Z)(Os,Ds,Ls,!1,null,"362f5b54",null),Ns=As.exports,Rs=function(){var e=this,t=e._self._c;return e.topics.length?t("section",{staticClass:"mentions"},[t("LinkableHeading",{attrs:{anchor:"mentions"}},[e._v(" "+e._s(e.$t("mentioned-in"))+" ")]),e._l(e.topics,(function(e){return t("Mention",{key:e.identifier,attrs:{url:e.url,title:e.title,role:e.role,kind:e.kind}})}))],2):e._e()},Bs=[],Es=function(){var e=this,t=e._self._c;return t("div",{staticClass:"link-block"},[t("Reference",{staticClass:"link",attrs:{url:e.url,role:e.role,kind:e.kind}},[t("TopicLinkBlockIcon",{staticClass:"mention-icon",attrs:{role:e.role}}),e._v(" "+e._s(e.title)+" ")],1)],1)},Ms=[],zs=n(9882),Ks=n(2970),Zs={components:{Reference:zs.Z,TopicLinkBlockIcon:Ks.Z},mixins:[nn.Z],props:{url:{type:String,required:!0},title:{type:String,required:!0},role:{type:String,required:!1,default:"article"},kind:{type:String,required:!1,default:"article"}}},qs=Zs,js=(0,j.Z)(qs,Es,Ms,!1,null,"241f4141",null),Fs=js.exports,Hs={name:"MentionedIn",components:{LinkableHeading:tn.Z,Mention:Fs},mixins:[nn.Z],props:{mentions:{type:Array,required:!0}},computed:{topics(){return this.mentions&&this.mentions.length?this.mentions.slice(0,5).flatMap((e=>this.references[e])):[]}}},Vs=Hs,Ws=(0,j.Z)(Vs,Rs,Bs,!1,null,null,null),Us=Ws.exports,Qs={name:"PrimaryContent",components:{Attributes:ei,ContentNode:Ue.Z,Parameters:Pi,PropertyListKeyDetails:wi,PropertyTable:cs,RestBody:Ss,RestEndpoint:pi,RestParameters:$s,RestResponses:Ns,PossibleValues:oi,Mentions:Us},constants:{SectionKind:je},props:{sections:{type:Array,required:!0,validator:e=>e.every((({kind:e})=>Object.prototype.hasOwnProperty.call(je,e)))}},computed:{span(){return{large:9,medium:9,small:12}}},methods:{componentFor(e){return{[je.attributes]:ei,[je.content]:Ue.Z,[je.details]:wi,[je.parameters]:Pi,[je.properties]:cs,[je.restBody]:Ss,[je.restParameters]:$s,[je.restHeaders]:$s,[je.restCookies]:$s,[je.restEndpoint]:pi,[je.restResponses]:Ns,[je.possibleValues]:oi,[je.mentions]:Us}[e.kind]},propsFor(e){const{attributes:t,bodyContentType:n,content:i,details:s,items:a,kind:r,mimeType:o,parameters:l,title:c,tokens:d,values:u,mentions:h}=e;return{[je.attributes]:{attributes:t},[je.content]:{content:i},[je.details]:{details:s},[je.parameters]:{parameters:l},[je.possibleValues]:{values:u},[je.properties]:{properties:a,title:c},[je.restBody]:{bodyContentType:n,content:i,mimeType:o,parts:l,title:c},[je.restCookies]:{parameters:a,title:c},[je.restEndpoint]:{tokens:d,title:c},[je.restHeaders]:{parameters:a,title:c},[je.restParameters]:{parameters:a,title:c},[je.restResponses]:{responses:a,title:c},[je.mentions]:{mentions:h}}[r]}}},Gs=Qs,Xs=(0,j.Z)(Gs,In,$n,!1,null,"65c116be",null),Ys=Xs.exports,Js=function(){var e=this,t=e._self._c;return t("ContentTable",{attrs:{anchor:e.contentSectionData.anchor,title:e.$t(e.contentSectionData.title)}},e._l(e.sectionsWithSymbols,(function(e){return t("Section",{key:e.type,attrs:{title:e.title,anchor:e.anchor}},[t("List",{attrs:{symbols:e.symbols,type:e.type}})],1)})),1)},ea=[],ta=function(){var e=this,t=e._self._c;return t("ul",{ref:"apiChangesDiff",staticClass:"relationships-list",class:e.classes},e._l(e.symbols,(function(n){return t("li",{key:n.identifier,staticClass:"relationships-item"},[n.url?t("Reference",{staticClass:"link",attrs:{role:n.role,kind:n.kind,url:n.url}},[e._v(e._s(n.title))]):t("WordBreak",{attrs:{tag:"code"}},[e._v(e._s(n.title))]),n.conformance?t("ConditionalConstraints",{attrs:{constraints:n.conformance.constraints,prefix:n.conformance.conformancePrefix}}):e._e()],1)})),0)},na=[];const ia=3,sa={conformsTo:"conformance",inheritsFrom:"inheritance",inheritedBy:"inheritedBy"};var aa={name:"RelationshipsList",components:{ConditionalConstraints:Ye,Reference:zs.Z,WordBreak:Oe.Z},inject:["store","identifier"],mixins:[yt.JY,yt.PH],props:{symbols:{type:Array,required:!0},type:{type:String,required:!0}},data(){return{state:this.store.state}},computed:{classes({changeType:e,multipleLinesClass:t,displaysMultipleLinesAfterAPIChanges:n}){return[{inline:this.shouldDisplayInline,column:!this.shouldDisplayInline,[`changed changed-${e}`]:!!e,[t]:n}]},hasAvailabilityConstraints(){return this.symbols.some((e=>!!(e.conformance||{}).constraints))},changes({identifier:e,state:{apiChanges:t}}){return(t||{})[e]||{}},changeType({changes:e,type:t}){const n=sa[t];if(e.change!==Rt.yf.modified)return e.change;const i=e[n];if(!i)return;const s=(e,t)=>e.map(((e,n)=>[e,t[n]])),a=s(i.previous,i.new).some((([e,t])=>e.content?0===e.content.length&&t.content.length>0:!!t.content));return a?Rt.yf.added:Rt.yf.modified},shouldDisplayInline(){const{hasAvailabilityConstraints:e,symbols:t}=this;return t.length<=ia&&!e}}},ra=aa,oa=(0,j.Z)(ra,ta,na,!1,null,"ba5cad92",null),la=oa.exports,ca={name:"Relationships",mixins:[nn.Z],components:{ContentTable:cn,List:la,Section:yn},props:{sections:{type:Array,required:!0}},computed:{contentSectionData:()=>Gt.relationships,sectionsWithSymbols(){return this.sections.map((e=>({...e,symbols:e.identifiers.reduce(((e,t)=>this.references[t]?e.concat(this.references[t]):e),[])})))}}},da=ca,ua=(0,j.Z)(da,Js,ea,!1,null,null,null),ha=ua.exports,pa=n(7120),ga=function(){var e=this,t=e._self._c;return t("Section",{staticClass:"availability",attrs:{role:"complementary","aria-label":e.$t("sections.availability")}},[e._l(e.technologies,(function(n){return t("span",{key:n,staticClass:"technology"},[t("TechnologyIcon",{staticClass:"tech-icon"}),t("span",[e._v(e._s(n))])],1)})),e._l(e.platforms,(function(n){return t("span",{key:n.name,staticClass:"platform",class:e.changesClassesFor(n.name)},[t("AvailabilityRange",{attrs:{deprecatedAt:n.deprecatedAt,introducedAt:n.introducedAt,platformName:n.name}}),n.deprecatedAt?t("Badge",{attrs:{variant:"deprecated"}}):n.beta?t("Badge",{attrs:{variant:"beta"}}):e._e()],1)}))],2)},fa=[],ma=n(9001),ya=function(){var e=this,t=e._self._c;return t("span",{attrs:{role:"text","aria-label":e.ariaLabel,title:e.description}},[e._v(e._s(e.text))])},va=[],ba={name:"AvailabilityRange",props:{deprecatedAt:{type:String,required:!1},introducedAt:{type:String,required:!0},platformName:{type:String,required:!0}},computed:{ariaLabel(){const{deprecatedAt:e,description:t,text:n}=this;return[n].concat(e?this.$t("change-type.deprecated"):[]).concat(t).join(", ")},description(){const{deprecatedAt:e,introducedAt:t,platformName:n}=this;return e?this.$t("availability.introduced-and-deprecated",{name:n,introducedAt:t,deprecatedAt:e}):this.$t("availability.available-on",{name:n,introducedAt:t})},text(){const{deprecatedAt:e,introducedAt:t,platformName:n}=this;return e?`${n} ${t}–${e}`:`${n} ${t}+`}}},Ta=ba,_a=(0,j.Z)(Ta,ya,va,!1,null,null,null),Sa=_a.exports,ka={name:"Availability",mixins:[yt.JY],inject:["identifier","store"],components:{Badge:Ri.Z,AvailabilityRange:Sa,Section:ae,TechnologyIcon:ma.Z},props:{platforms:{type:Array,required:!0},technologies:{type:Array,required:!1}},data(){return{state:this.store.state}},methods:{changeFor(e){const{identifier:t,state:{apiChanges:n}}=this,{availability:i={}}=(n||{})[t]||{},s=i[e];if(s)return s.deprecated?Rt.yf.deprecated:s.introduced&&!s.introduced.previous?Rt.yf.added:Rt.yf.modified}}},Ca=ka,wa=(0,j.Z)(Ca,ga,fa,!1,null,"3da26baa",null),xa=wa.exports,Ia=function(){var e=this,t=e._self._c;return t("TopicsTable",{attrs:{anchor:e.contentSectionData.anchor,title:e.$t(e.contentSectionData.title),isSymbolDeprecated:e.isSymbolDeprecated,isSymbolBeta:e.isSymbolBeta,sections:e.sections}})},$a=[],Da={name:"SeeAlso",components:{TopicsTable:Sn},props:{isSymbolDeprecated:Boolean,isSymbolBeta:Boolean,sections:Sn.props.sections},computed:{contentSectionData:()=>Gt.seeAlso}},La=Da,Pa=(0,j.Z)(La,Ia,$a,!1,null,null,null),Oa=Pa.exports,Aa=function(){var e=this,t=e._self._c;return t("div",{staticClass:"topictitle"},[e.eyebrow?t("span",{staticClass:"eyebrow"},[e._v(e._s(e.eyebrow))]):e._e(),t("h1",{staticClass:"title"},[e._t("default"),e._t("after")],2)])},Na=[],Ra={name:"Title",props:{eyebrow:{type:String,required:!1}}},Ba=Ra,Ea=(0,j.Z)(Ba,Aa,Na,!1,null,"6630a012",null),Ma=Ea.exports,za=function(){var e=this,t=e._self._c;return t("TopicsTable",{attrs:{anchor:e.contentSectionData.anchor,title:e.$t(e.contentSectionData.title),isSymbolDeprecated:e.isSymbolDeprecated,isSymbolBeta:e.isSymbolBeta,sections:e.sections,topicStyle:e.topicStyle}})},Ka=[],Za={name:"Topics",components:{TopicsTable:Sn},computed:{contentSectionData:()=>Gt.topics},props:{isSymbolDeprecated:Boolean,isSymbolBeta:Boolean,sections:Sn.props.sections,topicStyle:{type:String,required:!0,validator:e=>Object.hasOwnProperty.call(Ae.o,e)}}},qa=Za,ja=(0,j.Z)(qa,za,Ka,!1,null,null,null),Fa=ja.exports,Ha=function(){var e=this,t=e._self._c;return t("div",{staticClass:"OnThisPageStickyContainer"},[e._t("default")],2)},Va=[],Wa={name:"OnThisPageStickyContainer"},Ua=Wa,Qa=(0,j.Z)(Ua,Ha,Va,!1,null,"39ac6ed0",null),Ga=Qa.exports,Xa=function(){var e=this,t=e._self._c;return t("NavMenuItems",{staticClass:"hierarchy",class:{"has-badge":e.hasBadge},attrs:{"aria-label":e.$t("documentation.nav.breadcrumbs")}},[e.root?t("HierarchyItem",{key:e.root.title,staticClass:"root-hierarchy",attrs:{url:e.addQueryParamsToUrl(e.root.url)}},[e._v(" "+e._s(e.root.title)+" ")]):e._e(),e._l(e.collapsibleItems,(function(n){return t("HierarchyItem",{key:n.title,attrs:{isCollapsed:"",url:e.addQueryParamsToUrl(n.url)}},[e._v(" "+e._s(n.title)+" ")])})),e.collapsibleItems.length?t("HierarchyCollapsedItems",{attrs:{topics:e.collapsibleItems}}):e._e(),e._l(e.nonCollapsibleItems,(function(n){return t("HierarchyItem",{key:n.title,attrs:{url:e.addQueryParamsToUrl(n.url)}},[e._v(" "+e._s(n.title)+" ")])})),e.smallViewport?e._e():t("HierarchyItem",{scopedSlots:e._u([{key:"tags",fn:function(){return[e.isSymbolDeprecated?t("Badge",{attrs:{variant:"deprecated"}}):e.isSymbolBeta?t("Badge",{attrs:{variant:"beta"}}):e._e(),e._l(e.currentTopicTags,(function(n){return t("Badge",{key:`${n.type}-${n.text}`,attrs:{variant:n.type}},[e._v(" "+e._s(n.text)+" ")])}))]},proxy:!0}],null,!1,1132636639)},[e._v(" "+e._s(e.currentTopicTitle)+" ")])],2)},Ya=[],Ja=n(2853),er=n(5381),tr=function(){var e=this,t=e._self._c;return t("li",{staticClass:"hierarchy-collapsed-items"},[t("button",{ref:"btn",staticClass:"toggle",class:{focused:!e.collapsed},on:{click:e.toggleCollapsed}},[t("span",{staticClass:"indicator"},[t("EllipsisIcon",{staticClass:"icon-inline toggle-icon"})],1)]),t("ul",{ref:"dropdown",staticClass:"dropdown",class:{collapsed:e.collapsed}},e._l(e.formattedTopics,(function(n){return t("li",{key:n.title,staticClass:"dropdown-item"},[t("NavMenuLink",{attrs:{url:n.url}},[e._v(" "+e._s(n.title)+" ")])],1)})),0)])},nr=[],ir=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"ellipsis-icon",attrs:{viewBox:"0 0 14 14",themeId:"ellipsis"}},[t("path",{attrs:{d:"m12.439 7.777v-1.554h-1.554v1.554zm-4.662 0v-1.554h-1.554v1.554zm-4.662 0v-1.554h-1.554v1.554z"}})])},sr=[],ar=n(9742),rr={name:"EllipsisIcon",components:{SVGIcon:ar.Z}},or=rr,lr=(0,j.Z)(or,ir,sr,!1,null,null,null),cr=lr.exports,dr=function(){var e=this,t=e._self._c;return e.isCurrent?t("span",{staticClass:"nav-menu-link current",attrs:{"aria-current":"page","aria-disabled":"true",role:"link"}},[e._t("default")],2):t("Reference",{staticClass:"nav-menu-link",attrs:{url:e.url,tabindex:"0"}},[e._t("default")],2)},ur=[],hr={name:"NavMenuLink",components:{Reference:zs.Z},computed:{isCurrent:({$route:e,url:t})=>"string"===typeof t?t===(0,O.Q2)(e.path,e.query).replace(/\/$/,""):t.name===e.name},props:{url:{type:[Object,String],required:!0}}},pr=hr,gr=(0,j.Z)(pr,dr,ur,!1,null,"2ad31daf",null),fr=gr.exports,mr={name:"HierarchyCollapsedItems",components:{EllipsisIcon:cr,NavMenuLink:fr},data:()=>({collapsed:!0}),props:{topics:{type:Array,required:!0}},watch:{collapsed(e,t){t&&!e?document.addEventListener("click",this.handleDocumentClick,!1):!t&&e&&document.removeEventListener("click",this.handleDocumentClick,!1)}},beforeDestroy(){document.removeEventListener("click",this.handleDocumentClick,!1)},computed:{formattedTopics:({$route:e,topics:t})=>t.map((t=>({...t,url:(0,O.Q2)(t.url,e.query)})))},methods:{handleDocumentClick(e){const{target:t}=e,{collapsed:n,$refs:{btn:i,dropdown:s}}=this,a=!i.contains(t)&&!s.contains(t);!n&&a&&(this.collapsed=!0)},toggleCollapsed(){this.collapsed=!this.collapsed}}},yr=mr,vr=(0,j.Z)(yr,tr,nr,!1,null,"7b701104",null),br=vr.exports,Tr=function(e,t){return e(t.$options.components.NavMenuItemBase,{tag:"component",staticClass:"hierarchy-item",class:[{collapsed:t.props.isCollapsed},t.data.staticClass]},[t.props.url?e("router-link",{staticClass:"parent item nav-menu-link",attrs:{to:t.props.url}},[t._t("default")],2):[e("span",{staticClass:"current item"},[t._t("default")],2),t._t("tags")]],2)},_r=[],Sr=n(535),kr=n(8785),Cr={name:"HierarchyItem",components:{NavMenuItemBase:Sr.Z,InlineChevronRightIcon:kr.Z},props:{isCollapsed:Boolean,url:{type:String,required:!1}}},wr=Cr,xr=(0,j.Z)(wr,Tr,_r,!0,null,"13293168",null),Ir=xr.exports;const $r=3,Dr=1;var Lr={name:"Hierarchy",components:{Badge:Ri.Z,NavMenuItems:Ja.Z,HierarchyCollapsedItems:br,HierarchyItem:Ir},constants:{MaxVisibleLinks:$r},inject:["store"],mixins:[nn.Z],props:{isSymbolDeprecated:Boolean,isSymbolBeta:Boolean,currentTopicTitle:{type:String,required:!0},parentTopics:{type:Array,default:()=>[]},currentTopicTags:{type:Array,default:()=>[]}},computed:{windowWidth:({store:e})=>e.state.contentWidth,root:({parentTopics:e})=>e[0],smallViewport:({windowWidth:e})=>e{const i=n?1:0;return e>1200?$r-i:e>1e3?$r-1-i:t?0:$r-2-i},collapsibleItems:({smallViewport:e,parentTopics:t,linksAfterCollapse:n,currentTopicTitle:i,$route:s})=>{const a=n?t.slice(Dr,-n):t.slice(Dr);return e&&a.push({title:i,url:s.path}),a},nonCollapsibleItems:({parentTopics:e,linksAfterCollapse:t})=>t?e.slice(Dr).slice(-t):[],hasBadge:({isSymbolDeprecated:e,isSymbolBeta:t,currentTopicTags:n})=>e||t||n.length},methods:{addQueryParamsToUrl(e){return(0,O.Q2)(e,this.$route.query)}}},Pr=Lr,Or=(0,j.Z)(Pr,Xa,Ya,!1,null,"d54f3438",null),Ar=Or.exports;const Nr=1050;var Rr={name:"DocumentationTopic",mixins:[P.Z],constants:{ON_THIS_PAGE_CONTAINER_BREAKPOINT:Nr},inject:{isTargetIDE:{default(){return!1}},store:{default(){return{reset(){},state:{}}}}},components:{Declaration:zt,OnThisPageStickyContainer:Ga,OnThisPageNav:qe,DocumentationHero:Pe,Abstract:Vt,Aside:B.Z,BetaLegalText:H,ContentNode:Ue.Z,DefaultImplementations:xn,DownloadButton:Wt.Z,LanguageSwitcher:fe,PrimaryContent:Ys,Relationships:ha,RequirementMetadata:pa.Z,Availability:xa,SeeAlso:Oa,Title:Ma,Topics:Fa,ViewMore:_e,WordBreak:Oe.Z,Hierarchy:Ar,InlinePlusCircleIcon:Kt.Z},props:{abstract:{type:Array,required:!1},conformance:{type:Object,required:!1},defaultImplementationsSections:{type:Array,required:!1},downloadNotAvailableSummary:{type:Array,required:!1},deprecationSummary:{type:Array,required:!1},diffAvailability:{type:Object,required:!1},modules:{type:Array,required:!1},hasNoExpandedDocumentation:{type:Boolean,required:!1},hierarchy:{type:Object,default:()=>({})},interfaceLanguage:{type:String,required:!0},identifier:{type:String,required:!0},isRequirement:{type:Boolean,default:()=>!1},platforms:{type:Array,required:!1},primaryContentSections:{type:Array,required:!1},references:{type:Object,required:!0},relationshipsSections:{type:Array,required:!1},roleHeading:{type:String,required:!1},title:{type:String,required:!0},topicSections:{type:Array,required:!1},topicSectionsStyle:{type:String,default:Ae.o.list},sampleCodeDownload:{type:Object,required:!1},seeAlsoSections:{type:Array,required:!1},languagePaths:{type:Object,default:()=>({})},tags:{type:Array,required:!0},objcPath:{type:String,required:!1},swiftPath:{type:String,required:!1},isSymbolDeprecated:{type:Boolean,required:!1},isSymbolBeta:{type:Boolean,required:!1},symbolKind:{type:String,default:""},role:{type:String,default:""},remoteSource:{type:Object,required:!1},pageImages:{type:Array,required:!1},enableMinimized:{type:Boolean,default:!1},enableOnThisPageNav:{type:Boolean,default:!1},disableHeroBackground:{type:Boolean,default:!1},standardColorIdentifier:{type:String,required:!1,validator:e=>Object.prototype.hasOwnProperty.call(Ie,e)},availableLocales:{type:Array,required:!1},hierarchyItems:{type:Array,default:()=>[]}},provide(){return{identifier:this.identifier,languages:new Set(Object.keys(this.languagePaths)),interfaceLanguage:this.interfaceLanguage,symbolKind:this.symbolKind,enableMinimized:this.enableMinimized}},data(){return{topicState:this.store.state,declListExpanded:!1}},computed:{normalizedSwiftPath:({swiftPath:e})=>(0,A.Jf)(e),normalizedObjcPath:({objcPath:e,swiftPath:t})=>(0,A.Jf)(e&&t?(0,O.Q2)(e,{language:D.Z.objectiveC.key.url}):e),defaultImplementationsCount(){return(this.defaultImplementationsSections||[]).reduce(((e,t)=>e+t.identifiers.length),0)},shouldShowAvailability:({platforms:e,technologies:t,enableMinimized:n})=>((e||[]).length||(t||[]).length)&&!n,hasBetaContent:({platforms:e})=>e&&e.length&&e.some((e=>e.beta)),pageTitle:({title:e})=>e,pageDescription:({abstract:e,extractFirstParagraphText:t})=>e?t(e):null,parentTopics:({hierarchyItems:e,references:t,hasOtherDeclarations:n,pageTitle:i})=>{const s=e.reduce(((e,n)=>{const i=t[n];if(i){const{title:t,url:n}=i;return e.concat({title:t,url:n})}return console.error(`Reference for "${n}" is missing`),e}),[]),a=(0,N.Z$)(s);return n&&a?.title===i&&s.pop(),s},shouldShowLanguageSwitcher:({objcPath:e,swiftPath:t,isTargetIDE:n,enableMinimized:i})=>!!(e&&t&&n)&&!i,enhanceBackground:({symbolKind:e,disableHeroBackground:t,enableMinimized:n})=>!t&&!n&&(!e||e===L.module),shortHero:({roleHeading:e,abstract:t,sampleCodeDownload:n,hasAvailability:i,shouldShowLanguageSwitcher:s,declarations:a})=>!!e+!!t+!!n+!!a.length+!!i+s<=1,technologies({modules:e=[]}){const t=e.reduce(((e,t)=>(e.push(t.name),e.concat(t.relatedModules||[]))),[]);return t.length>1?t:[]},titleBreakComponent:({enhanceBackground:e})=>e?"span":Oe.Z,hasPrimaryContent:({isRequirement:e,deprecationSummary:t,downloadNotAvailableSummary:n,primaryContentSectionsSanitized:i,shouldShowViewMoreLink:s})=>e||t&&t.length||n&&n.length||i.length||s,viewMoreLink:({interfaceLanguage:e,normalizedObjcPath:t,normalizedSwiftPath:n})=>e===D.Z.objectiveC.key.api?t:n,shouldShowViewMoreLink:({enableMinimized:e,hasNoExpandedDocumentation:t,viewMoreLink:n})=>e&&!t&&n,tagName(){return this.isSymbolDeprecated?this.$t("aside-kind.deprecated"):this.$t("aside-kind.beta")},pageIcon:({pageImages:e=[]})=>{const t=e.find((({type:e})=>"icon"===e));return t?t.identifier:null},shouldRenderTopicSection:({topicSectionsStyle:e,topicSections:t,enableMinimized:n})=>t&&e!==Ae.o.hidden&&!n,isOnThisPageNavVisible:({topicState:e})=>e.contentWidth>Nr,disableMetadata:({enableMinimized:e})=>e,primaryContentSectionsSanitized({primaryContentSections:e=[],symbolKind:t}){return e.filter((({kind:e})=>e===je.mentions?t!==L.module:e!==je.declarations))},declarations({primaryContentSections:e=[]}){return e.filter((({kind:e})=>e===je.declarations))},showOtherDeclarations({enableMinimized:e,hasOtherDeclarations:t}){return!e&&t},hasOtherDeclarations({declarations:e=[]}){return!!e.length&&e[0].declarations.some((e=>Object.prototype.hasOwnProperty.call(e,"otherDeclarations")))},declListToggleText({declListExpanded:e}){return e?this.$t("declarations.hide-other-declarations"):this.$t("declarations.show-all-declarations")}},methods:{extractProps(e){const{abstract:t,defaultImplementationsSections:n,deprecationSummary:i,downloadNotAvailableSummary:s,diffAvailability:a,hierarchy:r,identifier:{interfaceLanguage:o,url:l},metadata:{conformance:c,hasNoExpandedDocumentation:d,modules:u,availableLocales:h,platforms:p,required:g=!1,roleHeading:f,title:m="",tags:y=[],role:v,symbolKind:b="",remoteSource:T,images:_=[],color:{standardColorIdentifier:S}={}}={},primaryContentSections:k,relationshipsSections:C,references:w={},sampleCodeDownload:x,topicSectionsStyle:I,topicSections:$,seeAlsoSections:L,variantOverrides:P,variants:O=[]}=e,A=O.reduce(((e,t)=>t.traits.reduce(((e,n)=>n.interfaceLanguage?{...e,[n.interfaceLanguage]:(e[n.interfaceLanguage]||[]).concat(t.paths)}:e),e)),{}),{[D.Z.objectiveC.key.api]:[N]=[],[D.Z.swift.key.api]:[R]=[]}=A;return{abstract:t,conformance:c,defaultImplementationsSections:n,deprecationSummary:i,downloadNotAvailableSummary:s,diffAvailability:a,hasNoExpandedDocumentation:d,availableLocales:h,hierarchy:r,role:v,identifier:l,interfaceLanguage:o,isRequirement:g,modules:u,platforms:p,primaryContentSections:k,relationshipsSections:C,references:w,roleHeading:f,sampleCodeDownload:x,title:m,topicSections:$,topicSectionsStyle:I,seeAlsoSections:L,variantOverrides:P,symbolKind:b,tags:y.slice(0,1),remoteSource:T,pageImages:_,objcPath:N,swiftPath:R,standardColorIdentifier:S}},toggleDeclList(){this.declListExpanded=!this.declListExpanded}},created(){if(this.topicState.preferredLanguage===D.Z.objectiveC.key.url&&this.interfaceLanguage!==D.Z.objectiveC.key.api&&this.objcPath&&this.$route.query.language!==D.Z.objectiveC.key.url){const{query:e}=this.$route;this.$nextTick().then((()=>{this.$router.replace({path:(0,A.Jf)(this.objcPath),query:{...e,language:D.Z.objectiveC.key.url}})}))}R["default"].setAvailableLocales(this.availableLocales||[]),this.store.reset(),this.store.setReferences(this.references)},watch:{references(e){this.store.setReferences(e)},availableLocales(e){R["default"].setAvailableLocales(e)}}},Br=Rr,Er=(0,j.Z)(Br,I,$,!1,null,"59c7b9d0",null),Mr=Er.exports,zr=function(){var e=this,t=e._self._c;return t("div",{staticClass:"documentation-layout"},[e.isTargetIDE?e._e():t("Nav",{attrs:{diffAvailability:e.diffAvailability,interfaceLanguage:e.interfaceLanguage,objcPath:e.objcPath,swiftPath:e.swiftPath,displaySidenav:e.enableNavigator},on:{"toggle-sidenav":e.handleToggleSidenav},scopedSlots:e._u([{key:"title",fn:function({className:t}){return[e._t("nav-title",null,{className:t})]}}],null,!0)}),t("AdjustableSidebarWidth",e._g(e._b({staticClass:"full-width-container topic-wrapper",scopedSlots:e._u([{key:"aside",fn:function({scrollLockID:n,breakpoint:i}){return[t("NavigatorDataProvider",{ref:"NavigatorDataProvider",attrs:{"interface-language":e.interfaceLanguage,technologyUrl:e.technology?e.technology.url:"","api-changes-version":e.selectedAPIChangesVersion},scopedSlots:e._u([{key:"default",fn:function(s){return[t("div",{staticClass:"documentation-layout-aside"},[e.enableQuickNavigation?t("QuickNavigationModal",{attrs:{children:s.flatChildren,showQuickNavigationModal:e.showQuickNavigationModal,technology:e.technology?e.technology.title:""},on:{"update:showQuickNavigationModal":function(t){e.showQuickNavigationModal=t},"update:show-quick-navigation-modal":function(t){e.showQuickNavigationModal=t}}}):e._e(),t("transition",{attrs:{name:"delay-hiding"}},[t("Navigator",{directives:[{name:"show",rawName:"v-show",value:e.sidenavVisibleOnMobile||i===e.BreakpointName.large,expression:"sidenavVisibleOnMobile || breakpoint === BreakpointName.large"}],attrs:{flatChildren:s.flatChildren,"parent-topic-identifiers":e.parentTopicIdentifiers,technology:s.technology||e.technology,"is-fetching":s.isFetching,"error-fetching":s.errorFetching,"api-changes":s.apiChanges,references:e.references,"navigator-references":s.references,scrollLockID:n,"render-filter-on-top":i!==e.BreakpointName.large},on:{close:function(t){return e.handleToggleSidenav(i)}},scopedSlots:e._u([e.enableQuickNavigation?{key:"filter",fn:function(){return[t("QuickNavigationButton",{nativeOn:{click:function(t){return e.openQuickNavigationModal.apply(null,arguments)}}})]},proxy:!0}:null,{key:"navigator-head",fn:function({className:t}){return[e._t("nav-title",null,{className:t})]}}],null,!0)})],1)],1)]}}],null,!0)})]}}])},"AdjustableSidebarWidth",e.sidebarProps,!1),e.sidebarListeners),[t("PortalTarget",{attrs:{name:"modal-destination",multiple:""}}),e._t("content")],2)],1)},Kr=[],Zr=n(2433),qr=function(){var e=this,t=e._self._c;return t("button",{staticClass:"quick-navigation-open",attrs:{"aria-label":e.$t("quicknav.button.label"),title:e.$t("quicknav.button.title")}},[e._v(" / ")])},jr=[],Fr={name:"QuickNavigationButton"},Hr=Fr,Vr=(0,j.Z)(Hr,qr,jr,!1,null,"96c35eb8",null),Wr=Vr.exports,Ur=function(){var e=this,t=e._self._c;return t("GenericModal",{attrs:{isFullscreen:"",showClose:!1,visible:e.isVisible,backdropBackgroundColorOverride:"rgba(0, 0, 0, 0.7)"},on:{"update:visible":function(t){e.isVisible=t}}},[t("div",{staticClass:"quick-navigation"},[t("div",{staticClass:"quick-navigation__container",class:{focus:e.focusedInput}},[t("FilterInput",{staticClass:"quick-navigation__filter",attrs:{placeholder:e.placeholderText,focusInputWhenCreated:"",focusInputWhenEmpty:"",preventBorderStyle:"",selectInputOnFocus:""},on:{focus:function(t){e.focusedInput=!0},blur:function(t){e.focusedInput=!1}},nativeOn:{keydown:[function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"down",40,t.key,["Down","ArrowDown"])||t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:(t.preventDefault(),e.handleDownKeyInput.apply(null,arguments))},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")||t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:e.handleKeyEnter.apply(null,arguments)}]},scopedSlots:e._u([{key:"icon",fn:function(){return[t("div",{staticClass:"quick-navigation__magnifier-icon-container",class:{blue:e.userInput.length}},[t("MagnifierIcon")],1)]},proxy:!0}]),model:{value:e.userInput,callback:function(t){e.userInput=t},expression:"userInput"}}),t("div",{staticClass:"quick-navigation__match-list",class:{active:e.processedUserInput.length}},[e.noResultsWereFound?t("div",{staticClass:"no-results"},[t("p",[e._v(" "+e._s(e.$t("navigator.no-results"))+" ")])]):[t("div",e._b({staticClass:"quick-navigation__refs",on:{keydown:[function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"down",40,t.key,["Down","ArrowDown"])||t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:(t.preventDefault(),e.focusNext.apply(null,arguments))},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"up",38,t.key,["Up","ArrowUp"])||t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:(t.preventDefault(),e.focusPrev.apply(null,arguments))},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")||t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:e.handleKeyEnter.apply(null,arguments)}]}},"div",{[e.SCROLL_LOCK_DISABLE_ATTR]:!0},!1),e._l(e.filteredSymbols,(function(n,i){return t("Reference",{key:n.uid,ref:"match",refInFor:!0,staticClass:"quick-navigation__reference",attrs:{url:n.path,tabindex:e.focusedIndex===i?"0":"-1","data-index":i},nativeOn:{click:function(t){return e.closeQuickNavigationModal.apply(null,arguments)}}},[t("div",{staticClass:"quick-navigation__symbol-match",attrs:{role:"list"}},[t("div",{staticClass:"symbol-info"},[t("div",{staticClass:"symbol-name"},[t("TopicTypeIcon",{staticClass:"navigator-icon",attrs:{type:n.type}}),t("div",{staticClass:"symbol-title"},[t("span",{domProps:{textContent:e._s(e.formatSymbolTitle(n.title,0,n.start))}}),t("QuickNavigationHighlighter",{attrs:{text:n.substring,matcherText:e.processedUserInput}}),t("span",{domProps:{textContent:e._s(e.formatSymbolTitle(n.title,n.start+n.matchLength))}})],1)],1),t("div",{staticClass:"symbol-path"},e._l(n.parents,(function(i,s){return t("div",{key:i.title},[t("span",{staticClass:"parent-path",domProps:{textContent:e._s(i.title)}}),s!==n.parents.length-1?t("span",{staticClass:"parent-path",domProps:{textContent:e._s("/")}}):e._e()])})),0)])])])})),1),e.previewState?t("Preview",e._b({staticClass:"quick-navigation__preview",attrs:{json:e.previewJSON,state:e.previewState}},"Preview",{[e.SCROLL_LOCK_DISABLE_ATTR]:!0},!1)):e._e()]],2)],1)])])},Qr=[],Gr=function(){var e=this,t=e._self._c;return t("div",{staticClass:"filter",class:{focus:e.showSuggestedTags&&!e.preventBorderStyle},attrs:{role:"search",tabindex:"0","aria-labelledby":e.searchAriaLabelledBy},on:{"!blur":function(t){return e.handleBlur.apply(null,arguments)},"!focus":function(t){return e.handleFocus.apply(null,arguments)}}},[t("div",{class:["filter__wrapper",{"filter__wrapper--reversed":e.positionReversed,"filter__wrapper--no-border-style":e.preventBorderStyle}]},[t("div",{staticClass:"filter__top-wrapper"},[t("button",{staticClass:"filter__filter-button",class:{blue:e.inputIsNotEmpty},attrs:{"aria-hidden":"true",tabindex:"-1"},on:{click:e.focusInput,mousedown:function(e){e.preventDefault()}}},[e._t("icon",(function(){return[t("FilterIcon")]}))],2),t("div",{class:["filter__input-box-wrapper",{scrolling:e.isScrolling}],on:{scroll:e.handleScroll}},[e.hasSelectedTags?t("TagList",e._g(e._b({ref:"selectedTags",staticClass:"filter__selected-tags",attrs:{id:e.SelectedTagsId,input:e.input,tags:e.selectedTags,ariaLabel:e.$tc("filter.selected-tags",e.suggestedTags.length),activeTags:e.activeTags,translatableTags:e.translatableTags,areTagsRemovable:""},on:{"focus-prev":e.handleFocusPrevOnSelectedTags,"focus-next":e.focusInputFromTags,"reset-filters":e.resetFilters,"prevent-blur":function(t){return e.$emit("update:preventedBlur",!0)}}},"TagList",e.virtualKeyboardBind,!1),e.selectedTagsMultipleSelectionListeners)):e._e(),t("label",{staticClass:"filter__input-label",attrs:{id:"filter-label",for:e.FilterInputId,"data-value":e.modelValue,"aria-label":e.placeholder}},[t("input",e._g(e._b({directives:[{name:"model",rawName:"v-model",value:e.modelValue,expression:"modelValue"}],ref:"input",staticClass:"filter__input",attrs:{id:e.FilterInputId,placeholder:e.hasSelectedTags?"":e.placeholder,"aria-expanded":e.displaySuggestedTags?"true":"false",disabled:e.disabled,type:"text"},domProps:{value:e.modelValue},on:{focus:function(t){e.selectInputOnFocus&&e.selectInputAndTags()},keydown:[function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"down",40,t.key,["Down","ArrowDown"])?null:(t.preventDefault(),e.downHandler.apply(null,arguments))},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"up",38,t.key,["Up","ArrowUp"])?null:(t.preventDefault(),e.upHandler.apply(null,arguments))},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"left",37,t.key,["Left","ArrowLeft"])||"button"in t&&0!==t.button?null:e.leftKeyInputHandler.apply(null,arguments)},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"right",39,t.key,["Right","ArrowRight"])||"button"in t&&2!==t.button?null:e.rightKeyInputHandler.apply(null,arguments)},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"delete",[8,46],t.key,["Backspace","Delete","Del"])?null:e.deleteHandler.apply(null,arguments)},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"a",void 0,t.key,void 0)?null:t.metaKey?(t.preventDefault(),t.stopPropagation(),e.selectInputAndTags.apply(null,arguments)):null},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"a",void 0,t.key,void 0)?null:t.ctrlKey?(t.preventDefault(),e.selectInputAndTags.apply(null,arguments)):null},function(t){return t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:e.inputKeydownHandler.apply(null,arguments)},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")||t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:e.enterHandler.apply(null,arguments)},function(t){return t.shiftKey?t.ctrlKey||t.altKey||t.metaKey?null:e.inputKeydownHandler.apply(null,arguments):null},function(t){return t.shiftKey&&t.metaKey?t.ctrlKey||t.altKey?null:e.inputKeydownHandler.apply(null,arguments):null},function(t){return t.metaKey?t.ctrlKey||t.shiftKey||t.altKey?null:e.assignEventValues.apply(null,arguments):null},function(t){return t.ctrlKey?t.shiftKey||t.altKey||t.metaKey?null:e.assignEventValues.apply(null,arguments):null}],input:function(t){t.target.composing||(e.modelValue=t.target.value)}}},"input",e.AXinputProperties,!1),e.inputMultipleSelectionListeners))])],1),t("div",{staticClass:"filter__delete-button-wrapper"},[e.input.length||e.displaySuggestedTags||e.hasSelectedTags?t("button",{staticClass:"filter__delete-button",attrs:{"aria-label":e.$t("filter.reset-filter")},on:{click:function(t){return e.resetFilters(!0)},keydown:function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")||t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:(t.stopPropagation(),e.resetFilters(!0))},mousedown:function(e){e.preventDefault()}}},[t("ClearRoundedIcon")],1):e._e()])]),e.displaySuggestedTags?t("TagList",e._b({ref:"suggestedTags",staticClass:"filter__suggested-tags",attrs:{id:e.SuggestedTagsId,ariaLabel:e.$tc("filter.suggested-tags",e.suggestedTags.length),input:e.input,tags:e.suggestedTags,translatableTags:e.translatableTags},on:{"click-tags":function(t){return e.selectTag(t.tagName)},"prevent-blur":function(t){return e.$emit("update:preventedBlur",!0)},"focus-next":function(t){e.positionReversed?e.focusInput():e.$emit("focus-next")},"focus-prev":function(t){e.positionReversed?e.$emit("focus-prev"):e.focusInput()}}},"TagList",e.virtualKeyboardBind,!1)):e._e()],1)])},Xr=[],Yr=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"clear-rounded-icon",attrs:{viewBox:"0 0 16 16",themeId:"clear-rounded"}},[t("title",[e._v(e._s(e.$t("icons.clear")))]),t("path",{attrs:{d:"M14.55,0l1.45,1.45-6.56,6.55,6.54,6.54-1.45,1.45-6.53-6.53L1.47,15.99,.01,14.53l6.52-6.53L0,1.47,1.45,.02l6.55,6.54L14.55,0Z","fill-rule":"evenodd"}})])},Jr=[],eo={name:"ClearRoundedIcon",components:{SVGIcon:ar.Z}},to=eo,no=(0,j.Z)(to,Yr,Jr,!1,null,null,null),io=no.exports;function so(){if(window.getSelection)try{const{activeElement:e}=document;return e&&e.value?e.value.substring(e.selectionStart,e.selectionEnd):window.getSelection().toString()}catch(e){return""}else if(document.selection&&"Control"!==document.selection.type)return document.selection.createRange().text;return""}function ao(e){if("number"===typeof e.selectionStart)e.selectionStart=e.selectionEnd=e.value.length;else if("undefined"!==typeof e.createTextRange){e.focus();const t=e.createTextRange();t.collapse(!1),t.select()}}function ro(e){e.selectionStart=e.selectionEnd=0}function oo(e){return/^[\w\W\s]$/.test(e)}function lo(e){const t=e.match(/(.*)<\/data>/);try{return t?JSON.parse(t[1]):null}catch(n){return null}}function co(e){return"string"!==typeof e&&(e=JSON.stringify(e)),`${e}`}function uo(e,t,n,i){let s,a;return function(...r){function o(){clearTimeout(s),s=null}function l(){o(),e.apply(a,r)}if(a=this,!s||!n&&!i){if(!n)return o(),void(s=setTimeout(l,t));s=setTimeout(o,t),e.apply(a,r)}}}const ho=280,po=100;var go={data(){return{keyboardIsVirtual:!1,activeTags:[],initTagIndex:null,focusedTagIndex:null,metaKey:!1,shiftKey:!1,tabbing:!1,debouncedHandleDeleteTag:null}},constants:{DebounceDelay:ho,VirtualKeyboardThreshold:po},computed:{virtualKeyboardBind:({keyboardIsVirtual:e})=>({keyboardIsVirtual:e}),allSelectedTagsAreActive:({selectedTags:e,activeTags:t})=>e.every((e=>t.includes(e)))},methods:{selectRangeActiveTags(e=this.focusedTagIndex,t=this.selectedTags.length){this.activeTags=this.selectedTags.slice(e,t)},selectTag(e){this.updateSelectedTags([e]),this.setFilterInput("")},unselectActiveTags(){this.activeTags.length&&(this.deleteTags(this.activeTags),this.resetActiveTags())},async deleteHandler(e){this.activeTags.length>0&&this.setSelectedTags(this.selectedTags.filter((e=>!this.activeTags.includes(e)))),this.inputIsSelected()&&this.allSelectedTagsAreActive?(e.preventDefault(),await this.resetFilters()):0===this.$refs.input.selectionEnd&&this.hasSelectedTags&&(e.preventDefault(),this.keyboardIsVirtual?this.setSelectedTags(this.selectedTags.slice(0,-1)):this.$refs.selectedTags.focusLast()),this.unselectActiveTags()},leftKeyInputHandler(e){if(this.assignEventValues(e),this.hasSelectedTags){if(this.activeTags.length&&!this.shiftKey)return e.preventDefault(),void this.$refs.selectedTags.focusTag(this.activeTags[0]);if(this.shiftKey&&0===this.$refs.input.selectionStart&&"forward"!==this.$refs.input.selectionDirection)return null===this.focusedTagIndex&&(this.focusedTagIndex=this.selectedTags.length),this.focusedTagIndex>0&&(this.focusedTagIndex-=1),this.initTagIndex=this.selectedTags.length,void this.selectTagsPressingShift();(0===this.$refs.input.selectionEnd||this.inputIsSelected())&&this.$refs.selectedTags.focusLast()}},rightKeyInputHandler(e){if(this.assignEventValues(e),this.activeTags.length&&this.shiftKey&&this.focusedTagIndex=po&&(this.keyboardIsVirtual=!0)}),ho),setFilterInput(e){this.$emit("update:input",e)},setSelectedTags(e){this.$emit("update:selectedTags",e)},updateSelectedTags(e){this.setSelectedTags([...new Set([...this.selectedTags,...e])])},handleCopy(e){e.preventDefault();const t=[],n={tags:[],input:so()};if(this.activeTags.length){const e=this.activeTags;n.tags=e,t.push(e.join(" "))}return t.push(n.input),n.tags.length||n.input.length?(e.clipboardData.setData("text/html",co(n)),e.clipboardData.setData("text/plain",t.join(" ")),n):n},handleCut(e){e.preventDefault();const{input:t,tags:n}=this.handleCopy(e);if(!t&&!n.length)return;const i=this.selectedTags.filter((e=>!n.includes(e))),s=this.input.replace(t,"");this.setSelectedTags(i),this.setFilterInput(s)},handlePaste(e){e.preventDefault();const{types:t}=e.clipboardData;let n=[],i=e.clipboardData.getData("text/plain");if(t.includes("text/html")){const t=e.clipboardData.getData("text/html"),s=lo(t);s&&({tags:n=[],input:i=""}=s)}const s=so();i=s.length?this.input.replace(s,i):(0,hn.ZQ)(this.input,i,document.activeElement.selectionStart),this.setFilterInput(i.trim()),n.length&&(this.allSelectedTagsAreActive?this.setSelectedTags(n):this.updateSelectedTags(n)),this.resetActiveTags()},async handleDeleteTag({tagName:e,event:t={}}){const{key:n}=t;this.activeTags.length||this.deleteTags([e]),this.unselectActiveTags(),await this.$nextTick(),ao(this.$refs.input),this.hasSelectedTags&&(await this.focusInput(),"Backspace"===n&&ro(this.$refs.input))}},mounted(){window.visualViewport&&(window.visualViewport.addEventListener("resize",this.updateKeyboardType),this.$once("hook:beforeDestroy",(()=>{window.visualViewport.removeEventListener("resize",this.updateKeyboardType)})))}};const fo=1e3;var mo={constants:{ScrollingDebounceDelay:fo},data(){return{isScrolling:!1,scrollRemovedAt:0}},created(){this.deleteScroll=uo(this.deleteScroll,fo)},methods:{deleteScroll(){this.isScrolling=!1,this.scrollRemovedAt=Date.now()},handleScroll(e){const{target:t}=e;if(0!==t.scrollTop)return t.scrollTop=0,void e.preventDefault();const n=150,i=t.offsetWidth,s=i+n;if(t.scrollWidth0?this.focusIndex(this.focusedIndex-1):this.startingPointHook())},focusNext({metaKey:e,ctrlKey:t,shiftKey:n}){(e||t)&&n||(this.externalFocusChange=!1,this.focusedIndex0}},xo=function(){var e=this,t=e._self._c;return t("li",{staticClass:"tag",attrs:{role:"presentation"}},[t("button",{ref:"button",class:{focus:e.isActiveTag},attrs:{role:"option","aria-selected":e.ariaSelected,"aria-roledescription":"tag"},on:{focus:function(t){return e.$emit("focus",{event:t,tagName:e.name})},click:function(t){return t.preventDefault(),e.$emit("click",{event:t,tagName:e.name})},dblclick:function(t){t.preventDefault(),!e.keyboardIsVirtual&&e.deleteTag()},keydown:[function(t){return t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:e.$emit("keydown",{event:t,tagName:e.name})},function(t){return t.shiftKey?t.ctrlKey||t.altKey||t.metaKey?null:e.$emit("keydown",{event:t,tagName:e.name}):null},function(t){return t.shiftKey&&t.metaKey?t.ctrlKey||t.altKey?null:e.$emit("keydown",{event:t,tagName:e.name}):null},function(t){return t.metaKey?t.ctrlKey||t.shiftKey||t.altKey?null:e.$emit("keydown",{event:t,tagName:e.name}):null},function(t){return t.ctrlKey?t.shiftKey||t.altKey||t.metaKey?null:e.$emit("keydown",{event:t,tagName:e.name}):null},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"delete",[8,46],t.key,["Backspace","Delete","Del"])?null:(t.preventDefault(),e.deleteTag.apply(null,arguments))}],mousedown:function(t){return t.preventDefault(),e.focusButton.apply(null,arguments)},copy:e.handleCopy}},[e.isRemovableTag?e._e():t("span",{staticClass:"visuallyhidden"},[e._v(" "+e._s(e.$t("filter.add-tag"))+" - ")]),e.isTranslatableTag?[e._v(" "+e._s(e.$t(e.name))+" ")]:[e._v(" "+e._s(e.name)+" ")],e.isRemovableTag?t("span",{staticClass:"visuallyhidden"},[e._v(" – "+e._s(e.$t("filter.tag-select-remove"))+" ")]):e._e()],2)])},Io=[],$o={name:"Tag",props:{name:{type:String,required:!0},isFocused:{type:Boolean,default:()=>!1},isRemovableTag:{type:Boolean,default:!1},isTranslatableTag:{type:Boolean,default:!1},isActiveTag:{type:Boolean,default:!1},activeTags:{type:Array,required:!1},keyboardIsVirtual:{type:Boolean,default:!1}},watch:{isFocused(e){e&&this.focusButton()}},mounted(){document.addEventListener("copy",this.handleCopy),document.addEventListener("cut",this.handleCut),document.addEventListener("paste",this.handlePaste),this.$once("hook:beforeDestroy",(()=>{document.removeEventListener("copy",this.handleCopy),document.removeEventListener("cut",this.handleCut),document.removeEventListener("paste",this.handlePaste)}))},methods:{isCurrentlyActiveElement(){return document.activeElement===this.$refs.button},handleCopy(e){if(!this.isCurrentlyActiveElement())return;e.preventDefault();let t=[];t=this.activeTags.length>0?this.activeTags:[this.name],e.clipboardData.setData("text/html",co({tags:t})),e.clipboardData.setData("text/plain",t.join(" "))},handleCut(e){this.isCurrentlyActiveElement()&&this.isRemovableTag&&(this.handleCopy(e),this.deleteTag(e))},handlePaste(e){this.isCurrentlyActiveElement()&&this.isRemovableTag&&(e.preventDefault(),this.deleteTag(e),this.$emit("paste-content",e))},deleteTag(e){this.$emit("delete-tag",{tagName:this.name,event:e}),this.$emit("prevent-blur")},focusButton(e={}){this.keyboardIsVirtual||this.$refs.button.focus(),0===e.buttons&&this.isFocused&&this.deleteTag(e)}},computed:{ariaSelected:({isActiveTag:e,isRemovableTag:t})=>t?e?"true":"false":null}},Do=$o,Lo=(0,j.Z)(Do,xo,Io,!1,null,"7e76f326",null),Po=Lo.exports,Oo={name:"Tags",mixins:[mo,wo],props:{tags:{type:Array,default:()=>[]},activeTags:{type:Array,default:()=>[]},translatableTags:{type:Array,default:()=>[]},ariaLabel:{type:String,required:!1},id:{type:String,required:!1},input:{type:String,default:null},areTagsRemovable:{type:Boolean,default:!1},keyboardIsVirtual:{type:Boolean,default:!1}},components:{Tag:Po},methods:{focusTag(e){this.focusIndex(this.tags.indexOf(e))},startingPointHook(){this.$emit("focus-prev")},handleFocus(e,t){this.focusIndex(t),this.isScrolling=!1,this.$emit("focus",e)},endingPointHook(){this.$emit("focus-next")},resetScroll(){this.$refs["scroll-wrapper"].scrollLeft=0},handleKeydown(e){const{key:t}=e,n=this.tags[this.focusedIndex];oo(t)&&n&&this.$emit("delete-tag",{tagName:n.label||n,event:e})}},computed:{totalItemsToNavigate:({tags:e})=>e.length}},Ao=Oo,No=(0,j.Z)(Ao,ko,Co,!1,null,"1f2bd813",null),Ro=No.exports;const Bo=5,Eo="filter-input",Mo="selected-tags",zo="suggested-tags",Ko={autocorrect:"off",autocapitalize:"off",spellcheck:"false",role:"combobox","aria-haspopup":"true","aria-autocomplete":"none","aria-owns":"suggestedTags","aria-controls":"suggestedTags"};var Zo,qo,jo={name:"FilterInput",mixins:[mo,go],constants:{FilterInputId:Eo,SelectedTagsId:Mo,SuggestedTagsId:zo,AXinputProperties:Ko,TagLimit:Bo},components:{TagList:Ro,ClearRoundedIcon:io,FilterIcon:So},props:{positionReversed:{type:Boolean,default:()=>!1},tags:{type:Array,default:()=>[]},selectedTags:{type:Array,default:()=>[]},preventedBlur:{type:Boolean,default:()=>!1},placeholder:{type:String,default:()=>""},disabled:{type:Boolean,default:()=>!1},value:{type:String,default:()=>""},shouldTruncateTags:{type:Boolean,default:!1},focusInputWhenCreated:{type:Boolean,default:!1},focusInputWhenEmpty:{type:Boolean,default:!1},selectInputOnFocus:{type:Boolean,default:!1},preventBorderStyle:{type:Boolean,default:!1},translatableTags:{type:Array,default:()=>[]}},data(){return{resetedTagsViaDeleteButton:!1,FilterInputId:Eo,SelectedTagsId:Mo,SuggestedTagsId:zo,AXinputProperties:Ko,showSuggestedTags:!1}},computed:{hasSuggestedTags:({suggestedTags:e})=>e.length,hasSelectedTags:({selectedTags:e})=>e.length,inputIsNotEmpty:({input:e,hasSelectedTags:t})=>e.length||t,searchAriaLabelledBy:({hasSelectedTags:e})=>e?Eo.concat(" ",Mo):Eo,modelValue:{get:({value:e})=>e,set(e){this.$emit("input",e)}},input:({value:e})=>e,suggestedTags:({tags:e,selectedTags:t,shouldTruncateTags:n})=>{const i=e.filter((e=>!t.includes(e)));return n?i.slice(0,Bo):i},displaySuggestedTags:({showSuggestedTags:e,suggestedTags:t})=>e&&t.length>0,inputMultipleSelectionListeners:({resetActiveTags:e,handleCopy:t,handleCut:n,handlePaste:i})=>({click:e,copy:t,cut:n,paste:i}),selectedTagsMultipleSelectionListeners:({handleSingleTagClick:e,selectInputAndTags:t,handleDeleteTag:n,selectedTagsKeydownHandler:i,focusTagHandler:s,handlePaste:a})=>({"click-tags":e,"select-all":t,"delete-tag":n,keydown:i,focus:s,"paste-tags":a})},watch:{async selectedTags(){this.resetedTagsViaDeleteButton?this.resetedTagsViaDeleteButton=!1:this.$el.contains(document.activeElement)&&await this.focusInput(),this.displaySuggestedTags&&this.hasSuggestedTags&&this.$refs.suggestedTags.resetScroll()},suggestedTags:{immediate:!0,handler(e){this.$emit("suggested-tags",e)}},showSuggestedTags(e){this.$emit("show-suggested-tags",e)}},methods:{async focusInput(){await this.$nextTick(),this.$refs.input.focus(),!this.input&&this.resetActiveTags&&this.resetActiveTags()},async resetFilters(e=!1){if(this.setFilterInput(""),this.setSelectedTags([]),!e)return this.$emit("update:preventedBlur",!0),this.resetActiveTags&&this.resetActiveTags(),void await this.focusInput();this.resetedTagsViaDeleteButton=!0,this.showSuggestedTags=!1,this.$refs.input.blur()},focusFirstTag(e=(()=>{})){this.showSuggestedTags||(this.showSuggestedTags=!0),this.hasSuggestedTags&&this.$refs.suggestedTags?this.$refs.suggestedTags.focusFirst():e()},setFilterInput(e){this.$emit("input",e)},setSelectedTags(e){this.$emit("update:selectedTags",e)},deleteTags(e){this.setSelectedTags(this.selectedTags.filter((t=>!e.includes(t))))},async handleBlur(e){const t=e.relatedTarget;t&&t.matches&&t.matches("button, input, ul")&&this.$el.contains(t)||(await this.$nextTick(),this.resetActiveTags(),this.preventedBlur?this.$emit("update:preventedBlur",!1):(this.showSuggestedTags=!1,this.$emit("blur")))},downHandler(e){const t=()=>this.$emit("focus-next",e);this.positionReversed?t():this.focusFirstTag(t)},upHandler(e){const t=()=>this.$emit("focus-prev",e);this.positionReversed?this.focusFirstTag(t):t()},handleFocusPrevOnSelectedTags(){this.positionReversed?this.focusFirstTag((()=>this.$emit("focus-prev"))):this.$emit("focus-prev")},handleFocus(){this.showSuggestedTags=!0,this.$emit("focus")}},created(){this.focusInputWhenCreated&&document.activeElement!==this.$refs.input&&(this.inputIsNotEmpty||this.focusInputWhenEmpty)&&this.focusInput()}},Fo=jo,Ho=(0,j.Z)(Fo,Gr,Xr,!1,null,"9ad1ed4c",null),Vo=Ho.exports,Wo=n(5590),Uo={name:"QuickNavigationHighlighter",props:{text:{type:String,required:!0},matcherText:{type:String,default:""}},render(e){const{matcherText:t,text:n}=this,i=[];let s=0;return t?([...t].forEach((t=>{const a=n.toLowerCase().indexOf(t.toLowerCase(),s);s&&i.push(e("span",n.slice(s,a)));const r=a+1;i.push(e("span",{class:"match"},n.slice(a,r))),s=r})),e("p",{class:"highlight"},i)):e("span",{class:"highlight"},n)}},Qo=Uo,Go=(0,j.Z)(Qo,Zo,qo,!1,null,"4a2ce75d",null),Xo=Go.exports,Yo=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"magnifier-icon",attrs:{viewBox:"0 0 14 14",themeId:"magnifier"}},[t("path",{attrs:{d:"M15.0013 14.0319L10.9437 9.97424C11.8165 8.88933 12.2925 7.53885 12.2929 6.14645C12.2929 2.75841 9.53449 0 6.14645 0C2.75841 0 0 2.75841 0 6.14645C0 9.53449 2.75841 12.2929 6.14645 12.2929C7.57562 12.2929 8.89486 11.7932 9.94425 10.9637L14.0019 15.0213L15.0013 14.0319ZM6.13645 11.0736C4.83315 11.071 3.58399 10.5521 2.66241 9.63048C1.74084 8.70891 1.22194 7.45974 1.2193 6.15644C1.2193 3.44801 3.41802 1.23928 6.13645 1.23928C8.85488 1.23928 11.0536 3.44801 11.0536 6.15644C11.0636 8.86488 8.85488 11.0736 6.13645 11.0736Z"}})])},Jo=[],el={name:"MagnifierIcon",components:{SVGIcon:ar.Z}},tl=el,nl=(0,j.Z)(tl,Yo,Jo,!1,null,null,null),il=nl.exports,sl=function(){var e=this,t=e._self._c;return t("div",{staticClass:"preview"},[e.state===e.STATE.success?t("DocumentationTopic",e._b({attrs:{enableMinimized:""}},"DocumentationTopic",e.topicProps,!1)):e.state===e.STATE.loadingSlowly?t("div",{staticClass:"loading"},e._l(e.LOADER_ROW_STYLES,(function(e){return t("div",{key:e["--index"],staticClass:"loading-row",style:e})})),0):e.state===e.STATE.error?t("div",{staticClass:"unavailable"},[t("p",[e._v(e._s(e.$t("quicknav.preview-unavailable")))])]):e._e()],1)},al=[],rl=n(220);const{extractProps:ol}=Mr.methods,ll="hero",cl={error:"error",loading:"loading",loadingSlowly:"loadingSlowly",success:"success"},dl={...rl.Z,state:(0,x.d9)(rl.Z.state)};var ul={name:"QuickNavigationPreview",components:{DocumentationTopic:Mr},constants:{PreviewState:cl,PreviewStore:dl},data(){return{store:dl}},provide(){return{store:this.store}},props:{json:{type:Object,required:!1},state:{type:String,required:!0,validator:e=>Object.hasOwnProperty.call(cl,e)}},computed:{LOADER_ROW_STYLES:()=>[{"--index":0,width:"30%"},{"--index":1,width:"80%"},{"--index":2,width:"50%"}],STATE:()=>cl,topicProps:({json:e})=>{const t=ol(e),{sections:n=[]}=e;let{abstract:i}=t;const s=n.find((({kind:e})=>e===ll));return!i&&s&&(i=s.content),{...t,abstract:i}}}},hl=ul,pl=(0,j.Z)(hl,sl,al,!1,null,"779b8b01",null),gl=pl.exports;class fl{constructor(e){this.map=new Map,this.maxSize=e}get size(){return this.map.size}get(e){if(!this.map.has(e))return;const t=this.map.get(e);return this.map.delete(e),this.map.set(e,t),t}has(e){return this.map.has(e)}set(e,t){if(this.map.has(e)&&this.map.delete(e),this.map.set(e,t),this.map.size>this.maxSize){const e=this.map.keys().next().value;this.map.delete(e)}}*[Symbol.iterator](){yield*this.map}}const ml="",yl=32,vl="navigator-hide-button";function bl(e){return e.split("").reduce(((e,t)=>(e<<5)-e+t.charCodeAt(0)|0),0)}function Tl(e){const t={},n=e.length;for(let i=0;ie.parent===ml));const i=t[e];return i?(i.childUIDs||[]).map((e=>t[e])):[]}function Cl(e,t){const n=[],i=[e];let s=null;while(i.length){s=i.pop();const e=t[s];if(!e)return[];n.unshift(e),e.parent&&e.parent!==ml&&i.push(e.parent)}return n}function wl(e,t,n){const i=t[e];return i?kl(i.parent,t,n):[]}var xl=n(9652);const{PreviewState:Il}=gl.constants,$l="AbortError",Dl=20,Ll=1e3;var Pl={name:"QuickNavigationModal",components:{FilterInput:Vo,GenericModal:Wo.Z,MagnifierIcon:il,TopicTypeIcon:Ce.Z,QuickNavigationHighlighter:Xo,Reference:zs.Z,Preview:gl},mixins:[wo],created(){this.abortController=null,this.$cachedSymbolResults=new fl(Dl),this.loadingTimeout=null},data(){return{debouncedInput:"",userInput:"",focusedInput:!1,cachedSymbolResults:{},previewIsLoadingSlowly:!1,SCROLL_LOCK_DISABLE_ATTR:xl.n}},props:{children:{type:Array,required:!0},showQuickNavigationModal:{type:Boolean,required:!0},technology:{type:String,required:!1}},computed:{childrenMap({children:e}){return Tl(e)},filteredSymbols:({constructFuzzyRegex:e,children:t,fuzzyMatch:n,processedUserInput:i,childrenMap:s,orderSymbolsByPriority:a})=>{const r=t.filter((e=>"groupMarker"!==e.type&&null!=e.title));if(!i)return[];const o=n({inputLength:i.length,symbols:r,processedInputRegex:new RegExp(e(i),"i"),childrenMap:s}),l=[...new Map(o.map((e=>[e.path,e]))).values()];return a(l).slice(0,Dl)},placeholderText(){return this.technology?this.$t("filter.search-symbols",{technology:this.technology}):this.$t("filter.search")},isVisible:{get:({showQuickNavigationModal:e})=>e,set(e){this.$emit("update:showQuickNavigationModal",e)}},noResultsWereFound:({processedUserInput:e,totalItemsToNavigate:t})=>e.length&&!t,processedUserInput:({debouncedInput:e})=>e.replace(/\s/g,""),totalItemsToNavigate:({filteredSymbols:e})=>e.length,selectedSymbol:({filteredSymbols:e,focusedIndex:t})=>null!==t?e[t]:null,nextSymbol:({filteredSymbols:e,focusedIndex:t})=>{if(null===t)return null;let n=t+1;return n>=e.length&&(n=0),e[n]},focusedMatchElement:({$refs:e,focusedIndex:t})=>e.match.find((({$el:e})=>parseInt(e.dataset.index,10)===t)).$el,previewJSON:({cachedSymbolResults:e,selectedSymbol:t})=>t?(e[t.uid]||{}).json:null,previewState:({cachedSymbolResults:e,previewIsLoadingSlowly:t,selectedSymbol:n})=>n&&Object.hasOwnProperty.call(e,n.uid)?e[n.uid].success?Il.success:Il.error:t?Il.loadingSlowly:Il.loading},watch:{userInput:"debounceInput",focusedIndex(){this.focusedInput||(this.scrollIntoView(),this.focusReference())},selectedSymbol:"fetchSelectedSymbolData",$route:"closeQuickNavigationModal"},methods:{closeQuickNavigationModal(){this.$emit("update:showQuickNavigationModal",!1)},constructFuzzyRegex(e){return[...e].reduce(((t,n,i)=>t.concat(`[${n}]`).concat(i{const s=n.exec(t.title);if(!s)return!1;const a=s[0].length;return!(a>3*e)&&{uid:t.uid,title:t.title,path:t.path,parents:Cl(t.parent,i),type:t.type,inputLengthDifference:t.title.length-e,matchLength:a,matchLengthDifference:a-e,start:s.index,substring:s[0]}})).filter(Boolean)},handleKeyEnter(){!this.noResultsWereFound&&this.userInput.length&&(this.$router.push(this.filteredSymbols[this.focusedIndex].path),this.closeQuickNavigationModal())},orderSymbolsByPriority(e){return e.sort(((e,t)=>e.matchLengthDifference>t.matchLengthDifference?1:e.matchLengthDifferencet.start?1:e.startt.inputLengthDifference?1:e.inputLengthDifference{this.previewState===Il.loading&&(this.previewIsLoadingSlowly=!0)}),Ll),!this.selectedSymbol||this.$cachedSymbolResults.has(this.selectedSymbol.uid))return clearTimeout(this.loadingTimeout),void(this.previewIsLoadingSlowly=!1);const e=async e=>{if(e&&!this.$cachedSymbolResults.has(e.uid))try{const t=await(0,x.k_)(e.path,{signal:this.abortController.signal});this.$cachedSymbolResults.set(e.uid,{success:!0,json:t})}catch(t){t.name!==$l&&this.$cachedSymbolResults.set(e.uid,{success:!1})}finally{this.cachedSymbolResults=Object.freeze(Object.fromEntries(this.$cachedSymbolResults))}};this.abortController&&this.abortController.abort(),this.abortController=new AbortController,await Promise.all([e(this.selectedSymbol).finally((()=>{clearTimeout(this.loadingTimeout),this.previewIsLoadingSlowly=!1})),e(this.nextSymbol)])}}},Ol=Pl,Al=(0,j.Z)(Ol,Ur,Qr,!1,null,"2f89fac2",null),Nl=Al.exports,Rl=function(){var e=this,t=e._self._c;return t("div",{staticClass:"adjustable-sidebar-width",class:{dragging:e.isDragging,"sidebar-hidden":!e.enableNavigator||e.hiddenOnLarge}},[e.enableNavigator?t("div",{ref:"sidebar",staticClass:"sidebar"},[t("div",{ref:"aside",staticClass:"aside",class:e.asideClasses,style:e.asideStyles,attrs:{"aria-hidden":e.hiddenOnLarge?"true":null},on:{transitionstart:function(t){return t.target!==t.currentTarget?null:e.trackTransitionStart.apply(null,arguments)},transitionend:function(t){return t.target!==t.currentTarget?null:e.trackTransitionEnd.apply(null,arguments)}}},[e._t("aside",null,{animationClass:"aside-animated-child",scrollLockID:e.scrollLockID,breakpoint:e.breakpoint})],2),e.fixedWidth?e._e():t("div",{staticClass:"resize-handle",on:{mousedown:function(t){return t.preventDefault(),e.startDrag.apply(null,arguments)},touchstart:function(t){return t.preventDefault(),e.startDrag.apply(null,arguments)}}})]):e._e(),t("div",{ref:"content",staticClass:"content"},[e._t("default")],2),e.enableNavigator?t("BreakpointEmitter",{attrs:{scope:e.BreakpointScopes.nav},on:{change:function(t){e.breakpoint=t}}}):e._e()],1)},Bl=[],El=n(7247),Ml=n(7188),zl=n(114),Kl=n(1147),Zl=n(1716);const ql="sidebar",jl=1521,Fl=543,Hl=400,Vl={touch:{move:"touchmove",end:"touchend"},mouse:{move:"mousemove",end:"mouseup"}},Wl=(e,t=window.innerWidth)=>{const n=Math.min(t,jl);return Math.floor(Math.min(n*(e/100),n))},Ul={medium:30,large:30},Ql={medium:50,large:40},Gl="sidebar-scroll-lock";var Xl={name:"AdjustableSidebarWidth",constants:{SCROLL_LOCK_ID:Gl},components:{BreakpointEmitter:Ml["default"]},inject:["store"],props:{shownOnMobile:{type:Boolean,default:!1},enableNavigator:{type:Boolean,default:!0},hiddenOnLarge:{type:Boolean,default:!1},fixedWidth:{type:Number,default:null}},data(){const e=window.innerWidth,t=window.innerHeight,n=er.L3.large,i=Wl(Ul[n]),s=Wl(Ql[n]),a=e>=jl?Fl:Hl,r=El.tO.get(ql,a);return{isDragging:!1,width:this.fixedWidth||Math.min(Math.max(r,i),s),isTouch:!1,windowWidth:e,windowHeight:t,breakpoint:n,noTransition:!1,isTransitioning:!1,isOpeningOnLarge:!1,focusTrapInstance:null,mobileTopOffset:0,topOffset:0}},computed:{minWidthPercent:({breakpoint:e})=>Ul[e]||0,maxWidthPercent:({breakpoint:e})=>Ql[e]||100,maxWidth:({maxWidthPercent:e,windowWidth:t,fixedWidth:n})=>Math.max(n,Wl(e,t)),minWidth:({minWidthPercent:e,windowWidth:t,fixedWidth:n})=>Math.min(n||t,Wl(e,t)),widthInPx:({width:e})=>`${e}px`,hiddenOnLargeThreshold:({minWidth:e})=>e/2,events:({isTouch:e})=>e?Vl.touch:Vl.mouse,asideStyles:({widthInPx:e,mobileTopOffset:t,topOffset:n,windowHeight:i})=>({width:e,"--top-offset":n?`${n}px`:null,"--top-offset-mobile":`${t}px`,"--app-height":`${i}px`}),asideClasses:({isDragging:e,shownOnMobile:t,noTransition:n,isTransitioning:i,hiddenOnLarge:s,mobileTopOffset:a,isOpeningOnLarge:r})=>({dragging:e,"show-on-mobile":t,"hide-on-large":s,"is-opening-on-large":r,"no-transition":n,"sidebar-transitioning":i,"has-mobile-top-offset":a}),scrollLockID:()=>Gl,BreakpointScopes:()=>er.lU},async mounted(){window.addEventListener("keydown",this.onEscapeKeydown),window.addEventListener("resize",this.storeWindowSize,{passive:!0}),window.addEventListener("orientationchange",this.storeWindowSize,{passive:!0}),this.storeTopOffset(),0===this.topOffset&&0===window.scrollY||window.addEventListener("scroll",this.storeTopOffset,{passive:!0}),this.$once("hook:beforeDestroy",(()=>{window.removeEventListener("keydown",this.onEscapeKeydown),window.removeEventListener("resize",this.storeWindowSize),window.removeEventListener("orientationchange",this.storeWindowSize),window.removeEventListener("scroll",this.storeTopOffset),this.shownOnMobile&&this.toggleScrollLock(!1),this.focusTrapInstance&&this.focusTrapInstance.destroy()})),await this.$nextTick(),this.focusTrapInstance=new zl.Z(this.$refs.aside)},watch:{$route:"closeMobileSidebar",width:{immediate:!0,handler:Be((function(e){this.emitEventChange(e)}),150)},windowWidth:"getWidthInCheck",async breakpoint(e){this.getWidthInCheck(),e===er.L3.large&&this.closeMobileSidebar(),this.noTransition=!0,await(0,Ee.J)(5),this.noTransition=!1},shownOnMobile:"handleExternalOpen",async isTransitioning(e){e?(await(0,Ee.X)(1e3),this.isTransitioning=!1):this.updateContentWidthInStore()},hiddenOnLarge(){this.isTransitioning=!0}},methods:{getWidthInCheck:uo((function(){this.width>this.maxWidth?this.width=this.maxWidth:this.widththis.maxWidth&&(i=this.maxWidth),this.hiddenOnLarge&&i>=this.hiddenOnLargeThreshold&&(this.$emit("update:hiddenOnLarge",!1),this.isOpeningOnLarge=!0),this.width=Math.max(i,this.minWidth),i<=this.hiddenOnLargeThreshold&&this.$emit("update:hiddenOnLarge",!0)},stopDrag(e){e.preventDefault(),this.isDragging&&(this.isDragging=!1,El.tO.set(ql,this.width),document.removeEventListener(this.events.move,this.handleDrag),document.removeEventListener(this.events.end,this.stopDrag),this.emitEventChange(this.width))},emitEventChange(e){this.$emit("width-change",e),this.updateContentWidthInStore()},getTopOffset(){const e=document.getElementById(Zl.EA);if(!e)return 0;const{y:t}=e.getBoundingClientRect();return Math.max(t,0)},handleExternalOpen(e){e&&(this.mobileTopOffset=this.getTopOffset()),this.toggleScrollLock(e)},async updateContentWidthInStore(){await this.$nextTick(),this.store.setContentWidth(this.$refs.content.offsetWidth)},async toggleScrollLock(e){const t=document.getElementById(this.scrollLockID);e?(await this.$nextTick(),xl.Z.lockScroll(t),this.focusTrapInstance.start(),Kl.Z.hide(this.$refs.aside)):(xl.Z.unlockScroll(t),this.focusTrapInstance.stop(),Kl.Z.show(this.$refs.aside))},storeTopOffset:Be((function(){this.topOffset=this.getTopOffset()}),60),async trackTransitionStart({propertyName:e}){"width"!==e&&"transform"!==e||(this.isTransitioning=!0)},trackTransitionEnd({propertyName:e}){"width"!==e&&"transform"!==e||(this.isTransitioning=!1,this.isOpeningOnLarge=!1)}}},Yl=Xl,Jl=(0,j.Z)(Yl,Rl,Bl,!1,null,"1a55f7f5",null),ec=Jl.exports,tc=function(){var e=this,t=e._self._c;return t("nav",{staticClass:"navigator",attrs:{"aria-labelledby":e.INDEX_ROOT_KEY}},[t("NavigatorCard",e._b({directives:[{name:"show",rawName:"v-show",value:!e.isFetching,expression:"!isFetching"}],attrs:{type:e.type,children:e.flatChildren,"active-path":e.activePath,scrollLockID:e.scrollLockID,"error-fetching":e.errorFetching,"render-filter-on-top":e.renderFilterOnTop,"api-changes":e.apiChanges,"navigator-references":e.navigatorReferences},on:{close:function(t){return e.$emit("close")}},scopedSlots:e._u([{key:"filter",fn:function(){return[e._t("filter")]},proxy:!0},{key:"navigator-head",fn:function(){return[e._t("navigator-head",null,{className:"nav-title"})]},proxy:!0}],null,!0)},"NavigatorCard",e.technologyProps,!1)),e.isFetching?t("LoadingNavigatorCard",{on:{close:function(t){return e.$emit("close")}}}):e._e(),t("div",{staticClass:"visuallyhidden",attrs:{"aria-live":"polite"}},[e._v(" "+e._s(e.$t("navigator.navigator-is",{state:e.isFetching?e.$t("navigator.state.loading"):e.$t("navigator.state.ready")}))+" ")])],1)},nc=[],ic=function(){var e=this,t=e._self._c;return t("BaseNavigatorCard",e._b({class:{"filter-on-top":e.renderFilterOnTop},on:{close:function(t){return e.$emit("close")}},scopedSlots:e._u([{key:"above-navigator-head",fn:function(){return[e._t("above-navigator-head")]},proxy:!0},{key:"navigator-head",fn:function(){return[e._t("navigator-head")]},proxy:!0},{key:"body",fn:function({className:n}){return[e._t("post-head"),t("div",{class:n,on:{"!keydown":[function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"up",38,t.key,["Up","ArrowUp"])?null:t.altKey?(t.preventDefault(),e.focusFirst.apply(null,arguments)):null},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"down",40,t.key,["Down","ArrowDown"])?null:t.altKey?(t.preventDefault(),e.focusLast.apply(null,arguments)):null},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"up",38,t.key,["Up","ArrowUp"])||t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:(t.preventDefault(),e.focusPrev.apply(null,arguments))},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"down",40,t.key,["Down","ArrowDown"])||t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:(t.preventDefault(),e.focusNext.apply(null,arguments))}]}},[e.technology?t("Reference",{class:["technology-title",{"router-link-exact-active":e.isTechnologyRoute}],attrs:{id:e.INDEX_ROOT_KEY,url:e.technologyPath},nativeOn:{click:function(t){return t.altKey?(t.preventDefault(),e.toggleAllNodes.apply(null,arguments)):null}}},[t("h2",{staticClass:"card-link"},[e._v(" "+e._s(e.technology)+" ")]),e.isTechnologyBeta?t("Badge",{attrs:{variant:"beta"}}):e._e()],1):e._e(),t("DynamicScroller",{directives:[{name:"show",rawName:"v-show",value:e.hasNodes,expression:"hasNodes"}],ref:"scroller",staticClass:"scroller",attrs:{id:e.scrollLockID,"aria-label":e.$t("navigator.title"),items:e.navigatorItems,"min-item-size":e.itemSize,"emit-update":"","key-field":"uid"},on:{update:e.handleScrollerUpdate,"!keydown":[function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"up",38,t.key,["Up","ArrowUp"])?null:t.altKey?(t.preventDefault(),e.focusFirst.apply(null,arguments)):null},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"down",40,t.key,["Down","ArrowDown"])?null:t.altKey?(t.preventDefault(),e.focusLast.apply(null,arguments)):null},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"up",38,t.key,["Up","ArrowUp"])||t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:(t.preventDefault(),e.focusPrev.apply(null,arguments))},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"down",40,t.key,["Down","ArrowDown"])||t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:(t.preventDefault(),e.focusNext.apply(null,arguments))}]},nativeOn:{focusin:function(t){return e.handleFocusIn.apply(null,arguments)},focusout:function(t){return e.handleFocusOut.apply(null,arguments)}},scopedSlots:e._u([{key:"default",fn:function({item:n,active:i,index:s}){return[t("DynamicScrollerItem",e._b({ref:`dynamicScroller_${n.uid}`},"DynamicScrollerItem",{active:i,item:n,dataIndex:s},!1),[t("NavigatorCardItem",{attrs:{item:n,isRendered:i,"filter-pattern":e.filterPattern,"filter-text":e.debouncedFilter,"is-active":n.uid===e.activeUID,"is-bold":e.activePathMap[n.uid],expanded:e.openNodes[n.uid],"api-change":e.apiChangesObject[n.path],isFocused:e.focusedIndex===s,enableFocus:!e.externalFocusChange,"navigator-references":e.navigatorReferences},on:{toggle:e.toggle,"toggle-full":e.toggleFullTree,"toggle-siblings":e.toggleSiblings,navigate:e.handleNavigationChange,"focus-parent":e.focusNodeParent}})],1)]}}],null,!0)}),t("div",{staticClass:"visuallyhidden",attrs:{"aria-live":"polite"}},[e._v(" "+e._s(e.politeAriaLive)+" ")]),t("div",{staticClass:"no-items-wrapper",attrs:{"aria-live":"assertive"}},[t("p",{staticClass:"no-items"},[e._v(e._s(e.$t(e.assertiveAriaLive)))])])],1),e.errorFetching?e._e():t("div",{staticClass:"filter-wrapper"},[t("div",{staticClass:"navigator-filter"},[t("div",{staticClass:"input-wrapper"},[t("FilterInput",{staticClass:"filter-component",attrs:{tags:e.suggestedTags,translatableTags:e.translatableTags,"selected-tags":e.selectedTags,placeholder:e.$t("filter.title"),"should-keep-open-on-blur":!1,shouldTruncateTags:e.shouldTruncateTags,"position-reversed":!e.renderFilterOnTop},on:{"update:selectedTags":function(t){e.selectedTags=t},"update:selected-tags":function(t){e.selectedTags=t},clear:e.clearFilters},model:{value:e.filter,callback:function(t){e.filter=t},expression:"filter"}})],1),e._t("filter")],2)])]}}],null,!0)},"BaseNavigatorCard",{isTechnologyBeta:e.isTechnologyBeta,technologyPath:e.technologyPath},!1))},sc=[];function ac(){var e=window.navigator.userAgent,t=e.indexOf("MSIE ");if(t>0)return parseInt(e.substring(t+5,e.indexOf(".",t)),10);var n=e.indexOf("Trident/");if(n>0){var i=e.indexOf("rv:");return parseInt(e.substring(i+3,e.indexOf(".",i)),10)}var s=e.indexOf("Edge/");return s>0?parseInt(e.substring(s+5,e.indexOf(".",s)),10):-1}var rc=void 0;function oc(){oc.init||(oc.init=!0,rc=-1!==ac())}var lc={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"resize-observer",attrs:{tabindex:"-1"}})},staticRenderFns:[],_scopeId:"data-v-b329ee4c",name:"resize-observer",methods:{compareAndNotify:function(){this._w===this.$el.offsetWidth&&this._h===this.$el.offsetHeight||(this._w=this.$el.offsetWidth,this._h=this.$el.offsetHeight,this.$emit("notify"))},addResizeHandlers:function(){this._resizeObject.contentDocument.defaultView.addEventListener("resize",this.compareAndNotify),this.compareAndNotify()},removeResizeHandlers:function(){this._resizeObject&&this._resizeObject.onload&&(!rc&&this._resizeObject.contentDocument&&this._resizeObject.contentDocument.defaultView.removeEventListener("resize",this.compareAndNotify),delete this._resizeObject.onload)}},mounted:function(){var e=this;oc(),this.$nextTick((function(){e._w=e.$el.offsetWidth,e._h=e.$el.offsetHeight}));var t=document.createElement("object");this._resizeObject=t,t.setAttribute("aria-hidden","true"),t.setAttribute("tabindex",-1),t.onload=this.addResizeHandlers,t.type="text/html",rc&&this.$el.appendChild(t),t.data="about:blank",rc||this.$el.appendChild(t)},beforeDestroy:function(){this.removeResizeHandlers()}};function cc(e){e.component("resize-observer",lc),e.component("ResizeObserver",lc)}var dc={version:"0.4.5",install:cc},uc=null;"undefined"!==typeof window?uc=window.Vue:"undefined"!==typeof n.g&&(uc=n.g.Vue),uc&&uc.use(dc);function hc(e){return hc="function"===typeof Symbol&&"symbol"===typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"===typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},hc(e)}function pc(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function gc(e,t){for(var n=0;n2&&void 0!==arguments[2]?arguments[2]:{},r=function(r){for(var o=arguments.length,l=new Array(o>1?o-1:0),c=1;c1){var i=e.find((function(e){return e.isIntersecting}));i&&(t=i)}if(n.callback){var s=t.isIntersecting&&t.intersectionRatio>=n.threshold;if(s===n.oldResult)return;n.oldResult=s,n.callback(s,t)}}),this.options.intersection),t.context.$nextTick((function(){n.observer&&n.observer.observe(n.el)}))}}},{key:"destroyObserver",value:function(){this.observer&&(this.observer.disconnect(),this.observer=null),this.callback&&this.callback._clear&&(this.callback._clear(),this.callback=null)}},{key:"threshold",get:function(){return this.options.intersection&&this.options.intersection.threshold||0}}]),e}();function Cc(e,t,n){var i=t.value;if(i)if("undefined"===typeof IntersectionObserver)console.warn("[vue-observe-visibility] IntersectionObserver API is not available in your browser. Please install this polyfill: https://github.com/w3c/IntersectionObserver/tree/master/polyfill");else{var s=new kc(e,i,n);e._vue_visibilityState=s}}function wc(e,t,n){var i=t.value,s=t.oldValue;if(!Sc(i,s)){var a=e._vue_visibilityState;i?a?a.createObserver(i,n):Cc(e,{value:i},n):xc(e)}}function xc(e){var t=e._vue_visibilityState;t&&(t.destroyObserver(),delete e._vue_visibilityState)}var Ic={bind:Cc,update:wc,unbind:xc};function $c(e){e.directive("observe-visibility",Ic)}var Dc={version:"0.4.6",install:$c},Lc=null;"undefined"!==typeof window?Lc=window.Vue:"undefined"!==typeof n.g&&(Lc=n.g.Vue),Lc&&Lc.use(Dc);var Pc=n(7274),Oc=n.n(Pc),Ac=n(144),Nc={itemsLimit:1e3};const Rc={items:{type:Array,required:!0},keyField:{type:String,default:"id"},direction:{type:String,default:"vertical",validator:e=>["vertical","horizontal"].includes(e)},listTag:{type:String,default:"div"},itemTag:{type:String,default:"div"}};function Bc(){return this.items.length&&"object"!==typeof this.items[0]}let Ec=!1;if("undefined"!==typeof window){Ec=!1;try{var Mc=Object.defineProperty({},"passive",{get(){Ec=!0}});window.addEventListener("test",null,Mc)}catch(hh){}}let zc=0;var Kc={name:"RecycleScroller",components:{ResizeObserver:lc},directives:{ObserveVisibility:Ic},props:{...Rc,itemSize:{type:Number,default:null},gridItems:{type:Number,default:void 0},itemSecondarySize:{type:Number,default:void 0},minItemSize:{type:[Number,String],default:null},sizeField:{type:String,default:"size"},typeField:{type:String,default:"type"},buffer:{type:Number,default:200},pageMode:{type:Boolean,default:!1},prerender:{type:Number,default:0},emitUpdate:{type:Boolean,default:!1},skipHover:{type:Boolean,default:!1},listTag:{type:String,default:"div"},itemTag:{type:String,default:"div"},listClass:{type:[String,Object,Array],default:""},itemClass:{type:[String,Object,Array],default:""}},data(){return{pool:[],totalSize:0,ready:!1,hoverKey:null}},computed:{sizes(){if(null===this.itemSize){const e={"-1":{accumulator:0}},t=this.items,n=this.sizeField,i=this.minItemSize;let s,a=1e4,r=0;for(let o=0,l=t.length;o{this.$_prerender=!1,this.updateVisibleItems(!0),this.ready=!0}))},activated(){const e=this.$_lastUpdateScrollPosition;"number"===typeof e&&this.$nextTick((()=>{this.scrollToPosition(e)}))},beforeDestroy(){this.removeListeners()},methods:{addView(e,t,n,i,s){const a={item:n,position:0},r={id:zc++,index:t,used:!0,key:i,type:s};return Object.defineProperty(a,"nr",{configurable:!1,value:r}),e.push(a),a},unuseView(e,t=!1){const n=this.$_unusedViews,i=e.nr.type;let s=n.get(i);s||(s=[],n.set(i,s)),s.push(e),t||(e.nr.used=!1,e.position=-9999,this.$_views.delete(e.nr.key))},handleResize(){this.$emit("resize"),this.ready&&this.updateVisibleItems(!1)},handleScroll(e){this.$_scrollDirty||(this.$_scrollDirty=!0,requestAnimationFrame((()=>{this.$_scrollDirty=!1;const{continuous:e}=this.updateVisibleItems(!1,!0);e||(clearTimeout(this.$_refreshTimout),this.$_refreshTimout=setTimeout(this.handleScroll,100))})))},handleVisibilityChange(e,t){this.ready&&(e||0!==t.boundingClientRect.width||0!==t.boundingClientRect.height?(this.$emit("visible"),requestAnimationFrame((()=>{this.updateVisibleItems(!1)}))):this.$emit("hidden"))},updateVisibleItems(e,t=!1){const n=this.itemSize,i=this.gridItems||1,s=this.itemSecondarySize||n,a=this.$_computedMinItemSize,r=this.typeField,o=this.simpleArray?null:this.keyField,l=this.items,c=l.length,d=this.sizes,u=this.$_views,h=this.$_unusedViews,p=this.pool;let g,f,m,y,v,b;if(c)if(this.$_prerender)g=y=0,f=v=Math.min(this.prerender,l.length),m=null;else{const e=this.getScroll();if(t){let t=e.start-this.$_lastUpdateScrollPosition;if(t<0&&(t=-t),null===n&&te.start&&(s=a),a=~~((i+s)/2)}while(a!==n);for(a<0&&(a=0),g=a,m=d[c-1].accumulator,f=a;fc&&(f=c)),y=g;yc&&(f=c),y<0&&(y=0),v>c&&(v=c),m=Math.ceil(c/i)*n}}else g=f=y=v=m=0;f-g>Nc.itemsLimit&&this.itemsLimitError(),this.totalSize=m;const T=g<=this.$_endIndex&&f>=this.$_startIndex;if(this.$_continuous!==T){if(T){u.clear(),h.clear();for(let e=0,t=p.length;e=f)&&this.unuseView(b));const _=T?null:new Map;let S,k,C,w;for(let x=g;x=C.length)&&(b=this.addView(p,x,S,e,k),this.unuseView(b,!0),C=h.get(k)),b=C[w],b.item=S,b.nr.used=!0,b.nr.index=x,b.nr.key=e,b.nr.type=k,_.set(k,w+1),w++),u.set(e,b)),null===n?(b.position=d[x-1].accumulator,b.offset=0):(b.position=Math.floor(x/i)*n,b.offset=x%i*s)):b&&this.unuseView(b)}return this.$_startIndex=g,this.$_endIndex=f,this.emitUpdate&&this.$emit("update",g,f,y,v),clearTimeout(this.$_sortTimer),this.$_sortTimer=setTimeout(this.sortViews,300),{continuous:T}},getListenerTarget(){let e=Oc()(this.$el);return!window.document||e!==window.document.documentElement&&e!==window.document.body||(e=window),e},getScroll(){const{$el:e,direction:t}=this,n="vertical"===t;let i;if(this.pageMode){const t=e.getBoundingClientRect(),s=n?t.height:t.width;let a=-(n?t.top:t.left),r=n?window.innerHeight:window.innerWidth;a<0&&(r+=a,a=0),a+r>s&&(r=s-a),i={start:a,end:a+r}}else i=n?{start:e.scrollTop,end:e.scrollTop+e.clientHeight}:{start:e.scrollLeft,end:e.scrollLeft+e.clientWidth};return i},applyPageMode(){this.pageMode?this.addListeners():this.removeListeners()},addListeners(){this.listenerTarget=this.getListenerTarget(),this.listenerTarget.addEventListener("scroll",this.handleScroll,!!Ec&&{passive:!0}),this.listenerTarget.addEventListener("resize",this.handleResize)},removeListeners(){this.listenerTarget&&(this.listenerTarget.removeEventListener("scroll",this.handleScroll),this.listenerTarget.removeEventListener("resize",this.handleResize),this.listenerTarget=null)},scrollToItem(e){let t;t=null===this.itemSize?e>0?this.sizes[e-1].accumulator:0:Math.floor(e/this.gridItems)*this.itemSize,this.scrollToPosition(t)},scrollToPosition(e){const t="vertical"===this.direction?{scroll:"scrollTop",start:"top"}:{scroll:"scrollLeft",start:"left"};let n,i,s;if(this.pageMode){const a=Oc()(this.$el),r="HTML"===a.tagName?0:a[t.scroll],o=a.getBoundingClientRect(),l=this.$el.getBoundingClientRect(),c=l[t.start]-o[t.start];n=a,i=t.scroll,s=e+r+c}else n=this.$el,i=t.scroll,s=e;n[i]=s},itemsLimitError(){throw setTimeout((()=>{console.log("It seems the scroller element isn't scrolling, so it tries to render all the items at once.","Scroller:",this.$el),console.log("Make sure the scroller has a fixed height (or width) and 'overflow-y' (or 'overflow-x') set to 'auto' so it can scroll correctly and only render the items visible in the scroll viewport.")})),new Error("Rendered items limit reached")},sortViews(){this.pool.sort(((e,t)=>e.nr.index-t.nr.index))}}};function Zc(e,t,n,i,s,a,r,o,l,c){"boolean"!==typeof r&&(l=o,o=r,r=!1);const d="function"===typeof n?n.options:n;let u;if(e&&e.render&&(d.render=e.render,d.staticRenderFns=e.staticRenderFns,d._compiled=!0,s&&(d.functional=!0)),i&&(d._scopeId=i),a?(u=function(e){e=e||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext,e||"undefined"===typeof __VUE_SSR_CONTEXT__||(e=__VUE_SSR_CONTEXT__),t&&t.call(this,l(e)),e&&e._registeredComponents&&e._registeredComponents.add(a)},d._ssrRegister=u):t&&(u=r?function(e){t.call(this,c(e,this.$root.$options.shadowRoot))}:function(e){t.call(this,o(e))}),u)if(d.functional){const e=d.render;d.render=function(t,n){return u.call(n),e(t,n)}}else{const e=d.beforeCreate;d.beforeCreate=e?[].concat(e,u):[u]}return n}const qc=Kc;var jc=function(){var e,t,n=this,i=n.$createElement,s=n._self._c||i;return s("div",{directives:[{name:"observe-visibility",rawName:"v-observe-visibility",value:n.handleVisibilityChange,expression:"handleVisibilityChange"}],staticClass:"vue-recycle-scroller",class:(e={ready:n.ready,"page-mode":n.pageMode},e["direction-"+n.direction]=!0,e),on:{"&scroll":function(e){return n.handleScroll.apply(null,arguments)}}},[n.$slots.before?s("div",{ref:"before",staticClass:"vue-recycle-scroller__slot"},[n._t("before")],2):n._e(),n._v(" "),s(n.listTag,{ref:"wrapper",tag:"component",staticClass:"vue-recycle-scroller__item-wrapper",class:n.listClass,style:(t={},t["vertical"===n.direction?"minHeight":"minWidth"]=n.totalSize+"px",t)},[n._l(n.pool,(function(e){return s(n.itemTag,n._g({key:e.nr.id,tag:"component",staticClass:"vue-recycle-scroller__item-view",class:[n.itemClass,{hover:!n.skipHover&&n.hoverKey===e.nr.key}],style:n.ready?{transform:"translate"+("vertical"===n.direction?"Y":"X")+"("+e.position+"px) translate"+("vertical"===n.direction?"X":"Y")+"("+e.offset+"px)",width:n.gridItems?("vertical"===n.direction&&n.itemSecondarySize||n.itemSize)+"px":void 0,height:n.gridItems?("horizontal"===n.direction&&n.itemSecondarySize||n.itemSize)+"px":void 0}:null},n.skipHover?{}:{mouseenter:function(){n.hoverKey=e.nr.key},mouseleave:function(){n.hoverKey=null}}),[n._t("default",null,{item:e.item,index:e.nr.index,active:e.nr.used})],2)})),n._v(" "),n._t("empty")],2),n._v(" "),n.$slots.after?s("div",{ref:"after",staticClass:"vue-recycle-scroller__slot"},[n._t("after")],2):n._e(),n._v(" "),s("ResizeObserver",{on:{notify:n.handleResize}})],1)},Fc=[];jc._withStripped=!0;const Hc=void 0,Vc=void 0,Wc=void 0,Uc=!1,Qc=Zc({render:jc,staticRenderFns:Fc},Hc,qc,Vc,Uc,Wc,!1,void 0,void 0,void 0);var Gc={name:"DynamicScroller",components:{RecycleScroller:Qc},provide(){return"undefined"!==typeof ResizeObserver&&(this.$_resizeObserver=new ResizeObserver((e=>{requestAnimationFrame((()=>{if(Array.isArray(e))for(const t of e)if(t.target){const e=new CustomEvent("resize",{detail:{contentRect:t.contentRect}});t.target.dispatchEvent(e)}}))}))),{vscrollData:this.vscrollData,vscrollParent:this,vscrollResizeObserver:this.$_resizeObserver}},inheritAttrs:!1,props:{...Rc,minItemSize:{type:[Number,String],required:!0}},data(){return{vscrollData:{active:!0,sizes:{},validSizes:{},keyField:this.keyField,simpleArray:!1}}},computed:{simpleArray:Bc,itemsWithSize(){const e=[],{items:t,keyField:n,simpleArray:i}=this,s=this.vscrollData.sizes,a=t.length;for(let r=0;r=n)break;i+=t[o].size||this.minItemSize,s+=e[o].size||this.minItemSize}const r=s-i;0!==r&&(this.$el.scrollTop+=r)}},beforeCreate(){this.$_updates=[],this.$_undefinedSizes=0,this.$_undefinedMap={}},activated(){this.vscrollData.active=!0},deactivated(){this.vscrollData.active=!1},methods:{onScrollerResize(){const e=this.$refs.scroller;e&&this.forceUpdate(),this.$emit("resize")},onScrollerVisible(){this.$emit("vscroll:update",{force:!1}),this.$emit("visible")},forceUpdate(e=!0){(e||this.simpleArray)&&(this.vscrollData.validSizes={}),this.$emit("vscroll:update",{force:!0})},scrollToItem(e){const t=this.$refs.scroller;t&&t.scrollToItem(e)},getItemSize(e,t=undefined){const n=this.simpleArray?null!=t?t:this.items.indexOf(e):e[this.keyField];return this.vscrollData.sizes[n]||0},scrollToBottom(){if(this.$_scrollingToBottom)return;this.$_scrollingToBottom=!0;const e=this.$el;this.$nextTick((()=>{e.scrollTop=e.scrollHeight+5e3;const t=()=>{e.scrollTop=e.scrollHeight+5e3,requestAnimationFrame((()=>{e.scrollTop=e.scrollHeight+5e3,0===this.$_undefinedSizes?this.$_scrollingToBottom=!1:requestAnimationFrame(t)}))};requestAnimationFrame(t)}))}}};const Xc=Gc;var Yc=function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("RecycleScroller",e._g(e._b({ref:"scroller",attrs:{items:e.itemsWithSize,"min-item-size":e.minItemSize,direction:e.direction,"key-field":"id","list-tag":e.listTag,"item-tag":e.itemTag},on:{resize:e.onScrollerResize,visible:e.onScrollerVisible},scopedSlots:e._u([{key:"default",fn:function(t){var n=t.item,i=t.index,s=t.active;return[e._t("default",null,null,{item:n.item,index:i,active:s,itemWithSize:n})]}}],null,!0)},"RecycleScroller",e.$attrs,!1),e.listeners),[e._v(" "),n("template",{slot:"before"},[e._t("before")],2),e._v(" "),n("template",{slot:"after"},[e._t("after")],2),e._v(" "),n("template",{slot:"empty"},[e._t("empty")],2)],2)},Jc=[];Yc._withStripped=!0;const ed=void 0,td=void 0,nd=void 0,id=!1,sd=Zc({render:Yc,staticRenderFns:Jc},ed,Xc,td,id,nd,!1,void 0,void 0,void 0);var ad={name:"DynamicScrollerItem",inject:["vscrollData","vscrollParent","vscrollResizeObserver"],props:{item:{required:!0},watchData:{type:Boolean,default:!1},active:{type:Boolean,required:!0},index:{type:Number,default:void 0},sizeDependencies:{type:[Array,Object],default:null},emitResize:{type:Boolean,default:!1},tag:{type:String,default:"div"}},computed:{id(){if(this.vscrollData.simpleArray)return this.index;if(this.item.hasOwnProperty(this.vscrollData.keyField))return this.item[this.vscrollData.keyField];throw new Error(`keyField '${this.vscrollData.keyField}' not found in your item. You should set a valid keyField prop on your Scroller`)},size(){return this.vscrollData.validSizes[this.id]&&this.vscrollData.sizes[this.id]||0},finalActive(){return this.active&&this.vscrollData.active}},watch:{watchData:"updateWatchData",id(){this.size||this.onDataUpdate()},finalActive(e){this.size||(e?this.vscrollParent.$_undefinedMap[this.id]||(this.vscrollParent.$_undefinedSizes++,this.vscrollParent.$_undefinedMap[this.id]=!0):this.vscrollParent.$_undefinedMap[this.id]&&(this.vscrollParent.$_undefinedSizes--,this.vscrollParent.$_undefinedMap[this.id]=!1)),this.vscrollResizeObserver?e?this.observeSize():this.unobserveSize():e&&this.$_pendingVScrollUpdate===this.id&&this.updateSize()}},created(){if(!this.$isServer&&(this.$_forceNextVScrollUpdate=null,this.updateWatchData(),!this.vscrollResizeObserver)){for(const e in this.sizeDependencies)this.$watch((()=>this.sizeDependencies[e]),this.onDataUpdate);this.vscrollParent.$on("vscroll:update",this.onVscrollUpdate),this.vscrollParent.$on("vscroll:update-size",this.onVscrollUpdateSize)}},mounted(){this.vscrollData.active&&(this.updateSize(),this.observeSize())},beforeDestroy(){this.vscrollParent.$off("vscroll:update",this.onVscrollUpdate),this.vscrollParent.$off("vscroll:update-size",this.onVscrollUpdateSize),this.unobserveSize()},methods:{updateSize(){this.finalActive?this.$_pendingSizeUpdate!==this.id&&(this.$_pendingSizeUpdate=this.id,this.$_forceNextVScrollUpdate=null,this.$_pendingVScrollUpdate=null,this.computeSize(this.id)):this.$_forceNextVScrollUpdate=this.id},updateWatchData(){this.watchData&&!this.vscrollResizeObserver?this.$_watchData=this.$watch("item",(()=>{this.onDataUpdate()}),{deep:!0}):this.$_watchData&&(this.$_watchData(),this.$_watchData=null)},onVscrollUpdate({force:e}){!this.finalActive&&e&&(this.$_pendingVScrollUpdate=this.id),this.$_forceNextVScrollUpdate!==this.id&&!e&&this.size||this.updateSize()},onDataUpdate(){this.updateSize()},computeSize(e){this.$nextTick((()=>{if(this.id===e){const e=this.$el.offsetWidth,t=this.$el.offsetHeight;this.applySize(e,t)}this.$_pendingSizeUpdate=null}))},applySize(e,t){const n=~~("vertical"===this.vscrollParent.direction?t:e);n&&this.size!==n&&(this.vscrollParent.$_undefinedMap[this.id]&&(this.vscrollParent.$_undefinedSizes--,this.vscrollParent.$_undefinedMap[this.id]=void 0),this.$set(this.vscrollData.sizes,this.id,n),this.$set(this.vscrollData.validSizes,this.id,!0),this.emitResize&&this.$emit("resize",this.id))},observeSize(){this.vscrollResizeObserver&&this.$el.parentNode&&(this.vscrollResizeObserver.observe(this.$el.parentNode),this.$el.parentNode.addEventListener("resize",this.onResize))},unobserveSize(){this.vscrollResizeObserver&&(this.vscrollResizeObserver.unobserve(this.$el.parentNode),this.$el.parentNode.removeEventListener("resize",this.onResize))},onResize(e){const{width:t,height:n}=e.detail.contentRect;this.applySize(t,n)}},render(e){return e(this.tag,this.$slots.default)}};const rd=ad,od=void 0,ld=void 0,cd=void 0,dd=void 0,ud=Zc({},od,rd,ld,dd,cd,!1,void 0,void 0,void 0);function hd({idProp:e=(e=>e.item.id)}={}){const t={},n=new Ac["default"]({data(){return{store:t}}});return{data(){return{idState:null}},created(){this.$_id=null,this.$_getId="function"===typeof e?()=>e.call(this,this):()=>this[e],this.$watch(this.$_getId,{handler(e){this.$nextTick((()=>{this.$_id=e}))},immediate:!0}),this.$_updateIdState()},beforeUpdate(){this.$_updateIdState()},methods:{$_idStateInit(e){const i=this.$options.idState;if("function"===typeof i){const s=i.call(this,this);return n.$set(t,e,s),this.$_id=e,s}throw new Error("[mixin IdState] Missing `idState` function on component definition.")},$_updateIdState(){const n=this.$_getId();null==n&&console.warn(`No id found for IdState with idProp: '${e}'.`),n!==this.$_id&&(t[n]||this.$_idStateInit(n),this.idState=t[n])}}}}function pd(e,t){e.component(`${t}recycle-scroller`,Qc),e.component(`${t}RecycleScroller`,Qc),e.component(`${t}dynamic-scroller`,sd),e.component(`${t}DynamicScroller`,sd),e.component(`${t}dynamic-scroller-item`,ud),e.component(`${t}DynamicScrollerItem`,ud)}const gd={version:"1.1.2",install(e,t){const n=Object.assign({},{installComponents:!0,componentsPrefix:""},t);for(const i in n)"undefined"!==typeof n[i]&&(Nc[i]=n[i]);n.installComponents&&pd(e,n.componentsPrefix)}};let fd=null;function md(e){const t=(0,hn.RL)((0,hn.hr)(e));return new RegExp(t,"ig")}"undefined"!==typeof window?fd=window.Vue:"undefined"!==typeof n.g&&(fd=n.g.Vue),fd&&fd.use(gd);var yd,vd,bd=function(){var e=this,t=e._self._c;return t("BaseNavigatorCardItem",{class:{expanded:e.expanded,active:e.isActive,"is-group":e.isGroupMarker},style:{"--nesting-index":e.item.depth},attrs:{"data-nesting-index":e.item.depth,id:`container-${e.item.uid}`,"aria-hidden":e.isRendered?null:"true",hideNavigatorIcon:e.isGroupMarker},nativeOn:{keydown:[function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"left",37,t.key,["Left","ArrowLeft"])||"button"in t&&0!==t.button?null:(t.preventDefault(),e.handleLeftKeydown.apply(null,arguments))},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"right",39,t.key,["Right","ArrowRight"])||"button"in t&&2!==t.button||t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:(t.preventDefault(),e.handleRightKeydown.apply(null,arguments))},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")?null:(t.preventDefault(),e.clickReference.apply(null,arguments))},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"right",39,t.key,["Right","ArrowRight"])?null:t.altKey?"button"in t&&2!==t.button?null:(t.preventDefault(),e.toggleEntireTree.apply(null,arguments)):null}]},scopedSlots:e._u([{key:"depth-spacer",fn:function(){return[t("span",{attrs:{hidden:"",id:e.usageLabel}},[e._v(" "+e._s(e.$t("filter.navigate"))+" ")]),e.isParent?t("button",{staticClass:"tree-toggle",attrs:{tabindex:"-1","aria-labelledby":e.item.uid,"aria-expanded":e.expanded?"true":"false","aria-describedby":e.ariaDescribedBy},on:{click:[function(t){return t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:(t.preventDefault(),e.toggleTree.apply(null,arguments))},function(t){return t.altKey?(t.preventDefault(),e.toggleEntireTree.apply(null,arguments)):null},function(t){return t.metaKey?(t.preventDefault(),e.toggleSiblings.apply(null,arguments)):null}]}},[t("InlineChevronRightIcon",{staticClass:"icon-inline chevron",class:{rotate:e.expanded,animating:e.idState.isOpening}})],1):e._e()]},proxy:!0},{key:"navigator-icon",fn:function({className:n}){return[e.apiChange?t("span",{class:[{[`changed changed-${e.apiChange}`]:e.apiChange},n]}):t("TopicTypeIcon",{key:e.item.uid,class:n,attrs:{type:e.item.type,"image-override":e.item.icon?e.navigatorReferences[e.item.icon]:null,shouldCalculateOptimalWidth:!1}})]}},{key:"title-container",fn:function(){return[e.isParent?t("span",{attrs:{hidden:"",id:e.parentLabel}},[e._v(e._s(e.$tc("filter.parent-label",e.item.childUIDs.length,{"number-siblings":e.item.index+1,"total-siblings":e.item.siblingsCount,"parent-siblings":e.item.parent,"number-parent":e.item.childUIDs.length})))]):e._e(),e.isParent?e._e():t("span",{attrs:{id:e.siblingsLabel,hidden:""}},[e._v(" "+e._s(e.$t("filter.siblings-label",{"number-siblings":e.item.index+1,"total-siblings":e.item.siblingsCount,"parent-siblings":e.item.parent}))+" ")]),t(e.refComponent,{ref:"reference",tag:"component",staticClass:"leaf-link",class:{bolded:e.isBold},attrs:{id:e.item.uid,url:e.isGroupMarker?null:e.item.path||"",tabindex:e.isFocused?"0":"-1","aria-describedby":`${e.ariaDescribedBy} ${e.usageLabel}`},nativeOn:{click:[function(t){return t.ctrlKey||t.shiftKey||t.altKey||t.metaKey?null:e.handleClick.apply(null,arguments)},function(t){return t.altKey?(t.preventDefault(),e.toggleEntireTree.apply(null,arguments)):null}]}},[t("HighlightMatches",{attrs:{text:e.item.title,matcher:e.filterPattern}})],1),e.isDeprecated?t("Badge",{attrs:{variant:"deprecated"}}):e.isBeta?t("Badge",{attrs:{variant:"beta"}}):e._e()]},proxy:!0},{key:"content-container",fn:function(){return[e._t("card-item-content")]},proxy:!0}],null,!0)})},Td=[],_d=function(){var e=this,t=e._self._c;return t("div",{staticClass:"navigator-card-item"},[t("div",{staticClass:"head-wrapper"},[t("div",{staticClass:"depth-spacer"},[e._t("depth-spacer")],2),e.hideNavigatorIcon?e._e():t("div",{staticClass:"navigator-icon-wrapper"},[e._t("navigator-icon",null,{className:"navigator-icon"})],2),t("div",{staticClass:"title-container"},[e._t("title-container")],2),t("div",{staticClass:"content-container"},[e._t("content-container")],2)])])},Sd=[],kd={name:"BaseNavigatorCardItem",props:{hideNavigatorIcon:{type:Boolean,default:()=>!1}}},Cd=kd,wd=(0,j.Z)(Cd,_d,Sd,!1,null,"5e71f320",null),xd=wd.exports,Id={name:"HighlightMatch",props:{text:{type:String,required:!0},matcher:{type:RegExp,default:void 0}},render(e){const{matcher:t,text:n}=this;if(!t)return e("p",{class:"highlight"},n);const i=[];let s=0,a=null;const r=new RegExp(t,"gi");while(null!==(a=r.exec(n))){const t=a[0].length,r=a.index+t,o=n.slice(s,a.index);o&&i.push(e("span",o));const l=n.slice(a.index,r);l&&i.push(e("span",{class:"match"},l)),s=r}const o=n.slice(s,n.length);return o&&i.push(e("span",o)),e("p",{class:"highlight"},i)}},$d=Id,Dd=(0,j.Z)($d,yd,vd,!1,null,"7b81ca08",null),Ld=Dd.exports,Pd={name:"NavigatorCardItem",mixins:[hd({idProp:e=>e.item.uid})],components:{BaseNavigatorCardItem:xd,HighlightMatches:Ld,TopicTypeIcon:Ce.Z,InlineChevronRightIcon:kr.Z,Reference:zs.Z,Badge:Ri.Z},props:{isRendered:{type:Boolean,default:!1},item:{type:Object,required:!0},expanded:{type:Boolean,default:!1},filterPattern:{type:RegExp,default:void 0},filterText:{type:String,default:null},isActive:{type:Boolean,default:!1},isBold:{type:Boolean,default:!1},apiChange:{type:String,default:null,validator:e=>Rt.UG.includes(e)},isFocused:{type:Boolean,default:()=>!1},enableFocus:{type:Boolean,default:!0},navigatorReferences:{type:Object,default:()=>({})}},idState(){return{isOpening:!1}},computed:{isGroupMarker:({item:{type:e}})=>e===we.t.groupMarker,isParent:({item:e,isGroupMarker:t})=>!!e.childUIDs.length&&!t,parentLabel:({item:e})=>`label-parent-${e.uid}`,siblingsLabel:({item:e})=>`label-${e.uid}`,usageLabel:({item:e})=>`usage-${e.uid}`,ariaDescribedBy:({isParent:e,parentLabel:t,siblingsLabel:n})=>e?`${t}`:`${n}`,isBeta:({item:{beta:e}})=>!!e,isDeprecated:({item:{deprecated:e}})=>!!e,refComponent:({isGroupMarker:e})=>e?"h3":zs.Z},methods:{toggleTree(){this.idState.isOpening=!0,this.$emit("toggle",this.item)},toggleEntireTree(){this.idState.isOpening=!0,this.$emit("toggle-full",this.item)},toggleSiblings(){this.idState.isOpening=!0,this.$emit("toggle-siblings",this.item)},handleLeftKeydown(){this.expanded?this.toggleTree():this.$emit("focus-parent",this.item)},handleRightKeydown(){!this.expanded&&this.isParent&&this.toggleTree()},clickReference(){(this.$refs.reference.$el||this.$refs.reference).click()},focusReference(){(this.$refs.reference.$el||this.$refs.reference).focus()},handleClick(){this.isGroupMarker||this.$emit("navigate",this.item.uid)}},watch:{async isFocused(e){await(0,Ee.J)(8),e&&this.isRendered&&this.enableFocus&&this.focusReference()},async expanded(){await(0,Ee.J)(9),this.idState.isOpening=!1}}},Od=Pd,Ad=(0,j.Z)(Od,bd,Td,!1,null,"5148de22",null),Nd=Ad.exports,Rd=function(){var e=this,t=e._self._c;return t("div",{staticClass:"navigator-card"},[t("div",{staticClass:"navigator-card-full-height"},[t("div",{staticClass:"navigator-card-inner"},[t("div",{staticClass:"head-wrapper"},[e._t("above-navigator-head"),t("div",{staticClass:"head-inner"},[e._t("navigator-head"),t("button",{staticClass:"close-card",attrs:{id:e.SIDEBAR_HIDE_BUTTON_ID,"aria-label":e.$t("navigator.close-navigator")},on:{click:e.handleHideClick}},[t("InlineCloseIcon",{staticClass:"icon-inline close-icon"})],1)],2)],2),e._t("body",null,{className:"card-body"})],2)])])},Bd=[],Ed=n(7181),Md={name:"BaseNavigatorCard",components:{InlineCloseIcon:Ed.Z},data(){return{SIDEBAR_HIDE_BUTTON_ID:vl}},methods:{async handleHideClick(){this.$emit("close"),await this.$nextTick();const e=document.getElementById(Zl.Yj);e&&e.focus()}}},zd=Md,Kd=(0,j.Z)(zd,Rd,Bd,!1,null,"584a744a",null),Zd=Kd.exports;const qd={sampleCode:"filter.tags.sample-code",tutorials:"filter.tags.tutorials",articles:"filter.tags.articles",webServiceEndpoints:"filter.tags.web-service-endpoints"},jd=Rt.Ag,Fd={...qd,...jd,hideDeprecated:"filter.tags.hide-deprecated"},Hd={[we.t.article]:Fd.articles,[we.t.learn]:Fd.tutorials,[we.t.overview]:Fd.tutorials,[we.t.resources]:Fd.tutorials,[we.t.sampleCode]:Fd.sampleCode,[we.t.section]:Fd.tutorials,[we.t.tutorial]:Fd.tutorials,[we.t.project]:Fd.tutorials,[we.t.httpRequest]:Fd.webServiceEndpoints};var Vd={computed:{filteredChildren:({children:e,selectedTags:t,apiChanges:n,filterPattern:i,filterChildren:s})=>s(e,t,n,i),navigatorItems:({nodesToRender:e})=>e},methods:{filterChildren(e,t,n,i){const s=new Set(t);return e.filter((({title:e,path:t,type:a,deprecated:r,deprecatedChildrenCount:o,childUIDs:l})=>{const c=!i||i.test(e),d=r||o===l.length;let u=!0;if(s.size){if(u=s.has(Hd[a]),n&&!u){const e=n[t];u=s.has(Fd[e])}s.has(Fd.hideDeprecated)&&(u=!d)}const h=!n||!!n[t];return c&&u&&h}))}}},Wd={name:"TagsDataProvider",constants:{TOPIC_TYPE_TO_TAG:Hd},props:{shouldTruncateTags:{type:Boolean,default:!1}},computed:{availableTags:({renderableChildNodesMap:e,apiChangesObject:t,extractTags:n})=>n(e,t),suggestedTags({availableTags:e,selectedTags:t,hideAvailableTags:n}){return n||t.length?[]:e},translatableTags:({availableTags:e})=>[Fd.hideDeprecated,...e]},methods:{extractTags(e,t){const n=new Set(Object.values(qd)),i={type:[],changes:[],other:[]},s=new Set(Object.values(t));s.size?s.forEach((e=>{n.add(Fd[e])})):n.add(Fd.hideDeprecated);for(const a in e){if(!Object.hasOwnProperty.call(e,a))continue;if(!n.size)break;const{type:s,path:r,deprecated:o}=e[a],l=Hd[s];l&&n.has(l)&&(i.type.push(l),n.delete(l));const c=t[r];c&&n.has(Fd[c])&&(i.changes.push(Fd[c]),n.delete(Fd[c])),o&&n.has(Fd.hideDeprecated)&&(i.other.push(Fd.hideDeprecated),n.delete(Fd.hideDeprecated))}return Object.values(i).flat()}}};const Ud="navigator.state",Qd="navigator.no-results",Gd="navigator.no-children",Xd="navigator.error-fetching",Yd="navigator.items-found";var Jd={name:"NavigatorCard",constants:{STORAGE_KEY:Ud,ERROR_FETCHING:Xd,ITEMS_FOUND:Yd},components:{FilterInput:Vo,NavigatorCardItem:Nd,DynamicScroller:sd,DynamicScrollerItem:ud,BaseNavigatorCard:Zd,Reference:zs.Z,Badge:Ri.Z},props:{technologyPath:{type:String,default:""},children:{type:Array,required:!0},technology:{type:String,required:!1},activePath:{type:Array,required:!0},type:{type:String,required:!0},scrollLockID:{type:String,default:""},errorFetching:{type:Boolean,default:!1},apiChanges:{type:Object,default:null},isTechnologyBeta:{type:Boolean,default:!1},navigatorReferences:{type:Object,default:()=>{}},renderFilterOnTop:{type:Boolean,default:!1},hideAvailableTags:{type:Boolean,default:!1}},mixins:[wo,Vd,Wd],data(){return{filter:"",debouncedFilter:"",selectedTags:[],openNodes:Object.freeze({}),nodesToRender:Object.freeze([]),activeUID:null,lastFocusTarget:null,allNodesToggled:!1,INDEX_ROOT_KEY:ml}},computed:{isTechnologyRoute:({technologyPath:e,$route:t})=>e.toLowerCase()===t.path.toLowerCase(),politeAriaLive(){const{hasNodes:e,navigatorItems:t}=this;return e?this.$tc(Yd,t.length,{number:t.length}):""},assertiveAriaLive:({hasNodes:e,hasFilter:t,errorFetching:n})=>e?"":t?Qd:n?Xd:Gd,filterPattern:({debouncedFilter:e})=>e?new RegExp(md(e),"i"):null,itemSize:()=>yl,childrenMap({children:e}){return Tl(e)},activePathChildren({activeUID:e,childrenMap:t}){return e&&t[e]?Cl(e,t):[]},activePathMap:({activePathChildren:e})=>Object.fromEntries(e.map((({uid:e})=>[e,!0]))),activeIndex:({activeUID:e,navigatorItems:t})=>t.findIndex((t=>t.uid===e)),renderableChildNodesMap({hasFilter:e,childrenMap:t,deprecatedHidden:n,filteredChildren:i,removeDeprecated:s}){if(!e)return t;const a=i.length-1,r=new Set([]);for(let o=a;o>=0;o-=1){const e=i[o],a=t[e.groupMarkerUID];if(a&&r.add(a),r.has(e))continue;if(r.has(t[e.parent])&&e.type!==we.t.groupMarker){r.add(e);continue}let l=[];e.childUIDs.length&&(l=s(Sl(e.uid,t),n)),l.concat(Cl(e.uid,t)).forEach((e=>r.add(e)))}return Tl([...r])},nodeChangeDeps:({filteredChildren:e,activePathChildren:t,debouncedFilter:n,selectedTags:i})=>[e,t,n,i],hasFilter({debouncedFilter:e,selectedTags:t,apiChanges:n}){return Boolean(e.length||t.length||n)},deprecatedHidden:({selectedTags:e})=>e[0]===Fd.hideDeprecated,apiChangesObject(){return this.apiChanges||{}},hasNodes:({navigatorItems:e})=>!!e.length,totalItemsToNavigate:({navigatorItems:e})=>e.length,lastActivePathItem:({activePath:e})=>(0,N.Z$)(e)},created(){this.restorePersistedState()},watch:{filter:"debounceInput",nodeChangeDeps:"trackOpenNodes",activePath:"handleActivePathChange",apiChanges(e){e||(this.selectedTags=this.selectedTags.filter((e=>!Object.values(jd).includes(e))))},async activeUID(e,t){await this.$nextTick();const n=this.$refs[`dynamicScroller_${t}`];n&&n.updateSize&&n.updateSize()}},methods:{setUnlessEqual(e,t){(0,N.Xy)(t,this[e])||(this[e]=Object.freeze(t))},toggleAllNodes(){const e=this.children.filter((e=>e.parent===ml&&e.type!==we.t.groupMarker&&e.childUIDs.length));this.allNodesToggled=!this.allNodesToggled,this.allNodesToggled&&(this.openNodes={},this.generateNodesToRender()),e.forEach((e=>{this.toggleFullTree(e)}))},clearFilters(){this.filter="",this.debouncedFilter="",this.selectedTags=[]},scrollToFocus(){this.$refs.scroller.scrollToItem(this.focusedIndex)},debounceInput:uo((function(e){this.debouncedFilter=e,this.lastFocusTarget=null}),200),trackOpenNodes([e,t,n,i],[,s=[],a="",r=[]]=[]){if(n!==a&&!a&&this.getFromStorage("filter")||!(0,N.Xy)(i,r)&&!r.length&&this.getFromStorage("selectedTags",[]).length)return;const o=!(0,N.Xy)(s,t),{childrenMap:l}=this;let c=t;if(!(this.deprecatedHidden&&!this.debouncedFilter.length||o&&this.hasFilter)&&this.hasFilter){const t=new Set,n=e.length-1;for(let i=n;i>=0;i-=1){const n=e[i];t.has(l[n.parent])||t.has(n)||Cl(n.uid,l).slice(0,-1).forEach((e=>t.add(e)))}c=[...t]}const d=o?{...this.openNodes}:{},u=c.reduce(((e,t)=>(e[t.uid]=!0,e)),d);this.setUnlessEqual("openNodes",u),this.generateNodesToRender(),this.updateFocusIndexExternally()},toggle(e){const t=this.openNodes[e.uid];let n=[],i=[];if(t){const t=(0,x.d9)(this.openNodes),n=Sl(e.uid,this.childrenMap);n.forEach((({uid:e})=>{delete t[e]})),this.setUnlessEqual("openNodes",t),i=n.slice(1)}else this.setUnlessEqual("openNodes",{...this.openNodes,[e.uid]:!0}),n=kl(e.uid,this.childrenMap,this.children).filter((e=>this.renderableChildNodesMap[e.uid]));this.augmentRenderNodes({uid:e.uid,include:n,exclude:i})},toggleFullTree(e){const t=this.openNodes[e.uid],n=(0,x.d9)(this.openNodes),i=Sl(e.uid,this.childrenMap);let s=[],a=[];i.forEach((({uid:e})=>{t?delete n[e]:n[e]=!0})),t?s=i.slice(1):a=i.slice(1).filter((e=>this.renderableChildNodesMap[e.uid])),this.setUnlessEqual("openNodes",n),this.augmentRenderNodes({uid:e.uid,exclude:s,include:a})},toggleSiblings(e){const t=this.openNodes[e.uid],n=(0,x.d9)(this.openNodes),i=wl(e.uid,this.childrenMap,this.children);i.forEach((({uid:e,childUIDs:i,type:s})=>{if(i.length&&s!==we.t.groupMarker)if(t){const t=Sl(e,this.childrenMap);t.forEach((e=>{delete n[e.uid]})),delete n[e],this.augmentRenderNodes({uid:e,exclude:t.slice(1),include:[]})}else{n[e]=!0;const t=kl(e,this.childrenMap,this.children).filter((e=>this.renderableChildNodesMap[e.uid]));this.augmentRenderNodes({uid:e,exclude:[],include:t})}})),this.setUnlessEqual("openNodes",n),this.persistState()},removeDeprecated(e,t){return t?e.filter((({deprecated:e})=>!e)):e},generateNodesToRender(){const{children:e,openNodes:t,renderableChildNodesMap:n}=this;this.setUnlessEqual("nodesToRender",e.filter((e=>n[e.uid]&&(e.parent===ml||t[e.parent])))),this.persistState(),this.scrollToElement()},augmentRenderNodes({uid:e,include:t=[],exclude:n=[]}){const i=this.nodesToRender.findIndex((t=>t.uid===e));if(t.length){const e=t.filter((e=>!this.nodesToRender.includes(e))),n=this.nodesToRender.slice(0);n.splice(i+1,0,...e),this.setUnlessEqual("nodesToRender",n)}else if(n.length){const e=new Set(n);this.setUnlessEqual("nodesToRender",this.nodesToRender.filter((t=>!e.has(t))))}this.persistState()},getFromStorage(e,t=null){const n=El.y7.get(Ud,{}),i=n[this.technologyPath];return i?e?i[e]||t:i:t},persistState(){const e={path:this.lastActivePathItem},{path:t}=this.activeUID&&this.childrenMap[this.activeUID]||e,n={technology:this.technology,path:t,hasApiChanges:!!this.apiChanges,openNodes:Object.keys(this.openNodes).map(Number),nodesToRender:this.nodesToRender.map((({uid:e})=>e)),activeUID:this.activeUID,filter:this.filter,selectedTags:this.selectedTags},i={...El.y7.get(Ud,{}),[this.technologyPath]:n};El.y7.set(Ud,i)},clearPersistedState(){const e={...El.y7.get(Ud,{}),[this.technologyPath]:{}};El.y7.set(Ud,e)},restorePersistedState(){const e=this.getFromStorage();if(!e||e.path!==this.lastActivePathItem)return this.clearPersistedState(),void this.handleActivePathChange(this.activePath);const{technology:t,nodesToRender:n=[],filter:i="",hasAPIChanges:s=!1,activeUID:a=null,selectedTags:r=[],openNodes:o}=e;if(!n.length&&!i&&!r.length)return this.clearPersistedState(),void this.handleActivePathChange(this.activePath);const{childrenMap:l}=this,c=n.every((e=>l[e])),d=a?(this.childrenMap[a]||{}).path===this.lastActivePathItem:1===this.activePath.length;if(t!==this.technology||!c||s!==Boolean(this.apiChanges)||!d||a&&!i&&!r.length&&!n.includes(a))return this.clearPersistedState(),void this.handleActivePathChange(this.activePath);this.setUnlessEqual("openNodes",Object.fromEntries(o.map((e=>[e,!0])))),this.setUnlessEqual("nodesToRender",n.map((e=>l[e]))),this.selectedTags=r,this.filter=i,this.debouncedFilter=this.filter,this.activeUID=a,this.scrollToElement()},async scrollToElement(){if(await(0,Ee.J)(1),!this.$refs.scroller)return;const e=document.getElementById(this.activeUID);if(e&&0===this.getChildPositionInScroller(e))return;const t=this.nodesToRender.findIndex((e=>e.uid===this.activeUID));-1!==t?this.$refs.scroller.scrollToItem(t):this.hasFilter&&!this.deprecatedHidden&&this.$refs.scroller.scrollToItem(0)},getChildPositionInScroller(e){if(!e)return 0;const{paddingTop:t,paddingBottom:n}=getComputedStyle(this.$refs.scroller.$el),i={top:parseInt(t,10)||0,bottom:parseInt(n,10)||0},{y:s,height:a}=this.$refs.scroller.$el.getBoundingClientRect(),{y:r}=e.getBoundingClientRect();let o=0;e.offsetParent&&(o=e.offsetParent.offsetHeight);const l=r-s-i.top;return l<0?-1:l+o>=a-i.bottom?1:0},isInsideScroller(e){return!!this.$refs.scroller&&this.$refs.scroller.$el.contains(e)},handleFocusIn({target:e,relatedTarget:t}){if(this.lastFocusTarget=e,!t)return;const n=this.getChildPositionInScroller(e);if(0===n)return;const{offsetHeight:i}=e.offsetParent;this.$refs.scroller.$el.scrollBy({top:i*n,left:0})},handleFocusOut(e){e.relatedTarget&&(this.isInsideScroller(e.relatedTarget)||(this.lastFocusTarget=null))},handleScrollerUpdate:uo((async function(){await(0,Ee.X)(300),this.lastFocusTarget&&this.isInsideScroller(this.lastFocusTarget)&&document.activeElement!==this.lastFocusTarget&&this.lastFocusTarget.focus({preventScroll:!0})}),50),setActiveUID(e){this.activeUID=e},handleNavigationChange(e){const t=this.childrenMap[e].path;t.startsWith(this.technologyPath)&&(this.setActiveUID(e),this.$emit("navigate",t))},pathsToFlatChildren(e){const t=e.slice(0).reverse(),{childrenMap:n}=this;let i=this.children;const s=[];while(t.length){const e=t.pop(),a=i.find((t=>t.path===e));if(!a)break;s.push(a),t.length&&(i=a.childUIDs.map((e=>n[e])))}return s},handleActivePathChange(e){const t=this.childrenMap[this.activeUID],n=(0,N.Z$)(e);if(t){if(n===t.path)return;const e=wl(this.activeUID,this.childrenMap,this.children),i=kl(this.activeUID,this.childrenMap,this.children),s=Cl(this.activeUID,this.childrenMap),a=[...i,...e,...s].find((e=>e.path===n));if(a)return void this.setActiveUID(a.uid)}const i=this.pathsToFlatChildren(e);i.length?this.setActiveUID(i[i.length-1].uid):this.activeUID?this.setActiveUID(null):this.trackOpenNodes(this.nodeChangeDeps)},updateFocusIndexExternally(){this.externalFocusChange=!0,this.activeIndex>0?this.focusIndex(this.activeIndex):this.focusIndex(0)},focusNodeParent(e){const t=this.childrenMap[e.parent];if(!t)return;const n=this.nodesToRender.findIndex((e=>e.uid===t.uid));-1!==n&&this.focusIndex(n)}}},eu=Jd,tu=(0,j.Z)(eu,ic,sc,!1,null,"4fc101dd",null),nu=tu.exports,iu=function(){var e=this,t=e._self._c;return t("BaseNavigatorCard",e._b({on:{close:function(t){return e.$emit("close")}},scopedSlots:e._u([{key:"body",fn:function({className:n}){return[t("transition",{attrs:{name:"delay-visibility"}},[t("div",{staticClass:"loading-navigator",class:n,attrs:{"aria-hidden":"true"}},e._l(e.LOADER_ROWS,(function(e,n){return t("LoadingNavigatorItem",{key:n,attrs:{index:n,width:e.width,hideNavigatorIcon:e.hideNavigatorIcon}})})),1)])]}}])},"BaseNavigatorCard",e.$props,!1))},su=[],au=function(){var e=this,t=e._self._c;return t("BaseNavigatorCardItem",{staticClass:"loading-navigator-item",style:`--index: ${e.index};`,attrs:{hideNavigatorIcon:e.hideNavigatorIcon},scopedSlots:e._u([{key:"navigator-icon",fn:function({className:e}){return[t("div",{class:e})]}},{key:"title-container",fn:function(){return[t("div",{staticClass:"loader",style:{width:e.width}})]},proxy:!0}])})},ru=[],ou={name:"LoadingNavigatorItem",components:{BaseNavigatorCardItem:xd},props:{...xd.props,index:{type:Number,default:0},width:{type:String,default:"50%"}}},lu=ou,cu=(0,j.Z)(lu,au,ru,!1,null,"0de29914",null),du=cu.exports;const uu=[{width:"30%",hideNavigatorIcon:!0},{width:"80%"},{width:"50%"}];var hu,pu,gu={name:"LoadingNavigatorCard",components:{BaseNavigatorCard:Zd,LoadingNavigatorItem:du},data(){return{LOADER_ROWS:uu}}},fu=gu,mu=(0,j.Z)(fu,iu,su,!1,null,"3b7cf3a4",null),yu=mu.exports,vu={name:"Navigator",components:{NavigatorCard:nu,LoadingNavigatorCard:yu},data(){return{INDEX_ROOT_KEY:ml}},props:{flatChildren:{type:Array,required:!0},parentTopicIdentifiers:{type:Array,required:!0},technology:{type:Object,required:!1},isFetching:{type:Boolean,default:!1},references:{type:Object,default:()=>{}},navigatorReferences:{type:Object,default:()=>{}},scrollLockID:{type:String,default:""},errorFetching:{type:Boolean,default:!1},renderFilterOnTop:{type:Boolean,default:!1},apiChanges:{type:Object,default:null}},computed:{parentTopicReferences({references:e,parentTopicIdentifiers:t}){return t.reduce(((t,n)=>{const i=e[n];return i?t.concat(i):(console.error(`Reference for "${n}" is missing`),t)}),[])},activePath({parentTopicReferences:e,$route:{path:t}}){if(t=t.replace(/\/$/,"").toLowerCase(),!e.length)return[t];let n=1;return"technologies"===e[0].kind&&(n=2),e.slice(n).map((e=>e.url)).concat(t)},type:()=>we.t.module,technologyProps:({technology:e})=>e?{technology:e.title,technologyPath:e.path||e.url,isTechnologyBeta:e.beta}:null}},bu=vu,Tu=(0,j.Z)(bu,tc,nc,!1,null,"7c66a058",null),_u=Tu.exports,Su=n(5184),ku={name:"NavigatorDataProvider",props:{interfaceLanguage:{type:String,default:D.Z.swift.key.url},technologyUrl:{type:String,default:""},apiChangesVersion:{type:String,default:""}},data(){return{isFetching:!1,errorFetching:!1,isFetchingAPIChanges:!1,navigationIndex:{[D.Z.swift.key.url]:[]},navigationReferences:{},diffs:null}},computed:{flatChildren:({technologyWithChildren:e={}})=>_l(e.children||[],null,0,e.beta),technologyPath:({technologyUrl:e})=>{const t=/(\/documentation\/(?:[^/]+))\/?/.exec(e);return t?t[1]:""},technologyWithChildren({navigationIndex:e,interfaceLanguage:t,technologyPath:n}){let i=e[t]||[];return i.length||(i=e[D.Z.swift.key.url]||[]),i.find((e=>n.toLowerCase()===e.path.toLowerCase()))}},methods:{async fetchIndexData(){try{this.isFetching=!0;const{interfaceLanguages:e,references:t}=await(0,x.LR)({slug:this.$route.params.locale||""});this.navigationIndex=Object.freeze(e),this.navigationReferences=Object.freeze(t)}catch(hh){this.errorFetching=!0}finally{this.isFetching=!1}}},watch:{"$route.params.locale":{handler:"fetchIndexData",immediate:!0}},render(){return this.$scopedSlots.default({technology:this.technologyWithChildren,isFetching:this.isFetching,errorFetching:this.errorFetching,isFetchingAPIChanges:this.isFetchingAPIChanges,apiChanges:this.diffs,flatChildren:this.flatChildren,references:this.navigationReferences})}},Cu=ku,wu=(0,j.Z)(Cu,hu,pu,!1,null,null,null),xu=wu.exports,Iu=function(){var e=this,t=e._self._c;return t("NavBase",{staticClass:"documentation-nav",attrs:{breakpoint:e.BreakpointName.medium,hasOverlay:!1,hasNoBorder:e.hasNoBorder,isDark:e.isDark,isWideFormat:"",hasFullWidthBorder:"","aria-label":e.$t("api-reference"),showActions:e.hasMenuItems},scopedSlots:e._u([e.displaySidenav?{key:"pre-title",fn:function({closeNav:n,isOpen:i,currentBreakpoint:s,className:a}){return[t("div",{class:a},[t("div",{staticClass:"sidenav-toggle-wrapper"},[t("button",{staticClass:"sidenav-toggle",attrs:{"aria-label":e.$t("navigator.open-navigator"),id:e.baseNavOpenSidenavButtonId,tabindex:i?-1:null},on:{click:function(t){return t.preventDefault(),e.handleSidenavToggle(n,s)}}},[t("span",{staticClass:"sidenav-icon-wrapper"},[t("SidenavIcon",{staticClass:"icon-inline sidenav-icon"})],1)])])])]}}:null,{key:"default",fn:function(){return[e._t("title",null,{className:"nav-title"})]},proxy:!0},{key:"tray",fn:function({closeNav:n}){return[e.hasMenuItems?t("NavMenuItems",{staticClass:"nav-menu-settings"},[e.hasLanguageToggle?t("LanguageToggle",{attrs:{interfaceLanguage:e.interfaceLanguage,objcPath:e.objcPath,swiftPath:e.swiftPath,closeNav:n}}):e._e(),e._t("menu-items")],2):e._e(),e._t("tray-after")]}},{key:"after-content",fn:function(){return[e._t("after-content")]},proxy:!0}],null,!0)})},$u=[],Du=n(2586),Lu=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"sidenav-icon",attrs:{viewBox:"0 0 14 14",height:"14",themeId:"sidenav"}},[t("path",{attrs:{d:"M6.533 1.867h-6.533v10.267h14v-10.267zM0.933 11.2v-8.4h4.667v8.4zM13.067 11.2h-6.533v-8.4h6.533z"}}),t("path",{attrs:{d:"M1.867 5.133h2.8v0.933h-2.8z"}}),t("path",{attrs:{d:"M1.867 7.933h2.8v0.933h-2.8z"}})])},Pu=[],Ou={name:"SidenavIcon",components:{SVGIcon:ar.Z}},Au=Ou,Nu=(0,j.Z)(Au,Lu,Pu,!1,null,null,null),Ru=Nu.exports,Bu=function(){var e=this,t=e._self._c;return t("NavMenuItemBase",{staticClass:"nav-menu-setting language-container"},[t("div",{class:{"language-toggle-container":e.hasLanguages}},[t("select",{ref:"language-sizer",staticClass:"language-dropdown language-sizer",attrs:{"aria-hidden":"true",tabindex:"-1"}},[t("option",{key:e.currentLanguage.name,attrs:{selected:""}},[e._v(e._s(e.currentLanguage.name))])]),t("label",{staticClass:"nav-menu-setting-label",attrs:{for:e.hasLanguages?"language-toggle":null}},[e._v(e._s(e.$t("formats.colon",{content:e.$t("language")})))]),e.hasLanguages?t("select",{directives:[{name:"model",rawName:"v-model",value:e.languageModel,expression:"languageModel"}],staticClass:"language-dropdown nav-menu-link",style:`width: ${e.adjustedWidth}px`,attrs:{id:"language-toggle"},on:{change:[function(t){var n=Array.prototype.filter.call(t.target.options,(function(e){return e.selected})).map((function(e){var t="_value"in e?e._value:e.value;return t}));e.languageModel=t.target.multiple?n:n[0]},function(t){return e.pushRoute(e.currentLanguage.route)}]}},e._l(e.languages,(function(n){return t("option",{key:n.api,domProps:{value:n.api}},[e._v(" "+e._s(n.name)+" ")])})),0):t("span",{staticClass:"nav-menu-toggle-none current-language",attrs:{"aria-current":"page"}},[e._v(e._s(e.currentLanguage.name))]),e.hasLanguages?t("InlineChevronDownIcon",{staticClass:"toggle-icon icon-inline"}):e._e()],1),e.hasLanguages?t("div",{staticClass:"language-list-container"},[t("span",{staticClass:"nav-menu-setting-label"},[e._v(e._s(e.$t("formats.colon",{content:e.$t("language")})))]),t("ul",{staticClass:"language-list"},e._l(e.languages,(function(n){return t("li",{key:n.api,staticClass:"language-list-item"},[n.api===e.languageModel?t("span",{staticClass:"current-language",attrs:{"data-language":n.api,"aria-current":"page"}},[e._v(" "+e._s(n.name)+" ")]):t("a",{staticClass:"nav-menu-link",attrs:{href:"#"},on:{click:function(t){return t.preventDefault(),e.pushRoute(n.route)}}},[e._v(" "+e._s(n.name)+" ")])])})),0)]):e._e()])},Eu=[],Mu=n(5151),zu={name:"LanguageToggle",components:{InlineChevronDownIcon:Mu.Z,NavMenuItemBase:Sr.Z},inject:{store:{default(){return{setPreferredLanguage(){}}}}},props:{interfaceLanguage:{type:String,required:!0},objcPath:{type:String,required:!1},swiftPath:{type:String,required:!1},closeNav:{type:Function,default:()=>{}}},data(){return{languageModel:null,adjustedWidth:0}},mounted(){const e=Be((async()=>{await(0,Ee.J)(3),this.calculateSelectWidth()}),150);window.addEventListener("resize",e),window.addEventListener("orientationchange",e),this.$once("hook:beforeDestroy",(()=>{window.removeEventListener("resize",e),window.removeEventListener("orientationchange",e)}))},watch:{interfaceLanguage:{immediate:!0,handler(e){this.languageModel=e}},currentLanguage:{immediate:!0,handler:"calculateSelectWidth"}},methods:{getRoute(e){const t=e.query===D.Z.swift.key.url?void 0:e.query;return{query:{...this.$route.query,language:t},path:this.isCurrentPath(e.path)?null:(0,A.Jf)(e.path)}},async pushRoute(e){await this.closeNav(),this.store.setPreferredLanguage(e.query),this.$router.push(this.getRoute(e))},isCurrentPath(e){return this.$route.path.replace(/^\//,"")===e},async calculateSelectWidth(){await this.$nextTick(),this.adjustedWidth=this.$refs["language-sizer"].clientWidth+8}},computed:{languages(){return[{name:D.Z.swift.name,api:D.Z.swift.key.api,route:{path:this.swiftPath,query:D.Z.swift.key.url}},{name:D.Z.objectiveC.name,api:D.Z.objectiveC.key.api,route:{path:this.objcPath,query:D.Z.objectiveC.key.url}}]},currentLanguage:({languages:e,languageModel:t})=>e.find((e=>e.api===t)),hasLanguages:({objcPath:e,swiftPath:t})=>t&&e}},Ku=zu,Zu=(0,j.Z)(Ku,Bu,Eu,!1,null,"4323807e",null),qu=Zu.exports,ju={name:"DocumentationNav",components:{SidenavIcon:Ru,NavBase:Du.Z,NavMenuItems:Ja.Z,LanguageToggle:qu},props:{isDark:{type:Boolean,default:!1},hasNoBorder:{type:Boolean,default:!1},interfaceLanguage:{type:String,required:!1},objcPath:{type:String,required:!1},swiftPath:{type:String,required:!1},displaySidenav:{type:Boolean,default:!1}},computed:{baseNavOpenSidenavButtonId:()=>Zl.Yj,BreakpointName:()=>er.L3,hasLanguageToggle:({interfaceLanguage:e,swiftPath:t,objcPath:n})=>!(!e||!t&&!n),hasMenuItems:({hasLanguageToggle:e,$slots:t})=>!(!e&&!t["menu-items"])},methods:{async handleSidenavToggle(e,t){await e(),this.$emit("toggle-sidenav",t),await this.$nextTick();const n=document.getElementById(vl);n&&n.focus()}}},Fu=ju,Hu=(0,j.Z)(Fu,Iu,$u,!1,null,"5e58283e",null),Vu=Hu.exports;const Wu="navigator-hidden-large";var Uu={name:"DocumentationLayout",constants:{NAVIGATOR_HIDDEN_ON_LARGE_KEY:Wu},components:{Navigator:_u,AdjustableSidebarWidth:ec,NavigatorDataProvider:xu,Nav:Vu,QuickNavigationButton:Wr,QuickNavigationModal:Nl,PortalTarget:Zr.YC},mixins:[Su.Z],props:{enableNavigator:Boolean,diffAvailability:{type:Object,required:!1},interfaceLanguage:{type:String,required:!1},references:{type:Object,default:()=>{}},objcPath:{type:String,required:!1},swiftPath:{type:String,required:!1},selectedAPIChangesVersion:{type:String,required:!1},technology:{type:Object,require:!1},parentTopicIdentifiers:{type:Array,default:()=>[]},navigatorFixedWidth:{type:Number,default:null}},data(){return{sidenavVisibleOnMobile:!1,sidenavHiddenOnLarge:El.tO.get(Wu,!1),showQuickNavigationModal:!1,BreakpointName:er.L3}},computed:{enableQuickNavigation:({isTargetIDE:e})=>!e&&(0,w.$8)(["features","docs","quickNavigation","enable"],!0),sidebarProps:({sidenavVisibleOnMobile:e,enableNavigator:t,sidenavHiddenOnLarge:n,navigatorFixedWidth:i})=>t?{shownOnMobile:e,hiddenOnLarge:n,fixedWidth:i}:{enableNavigator:t},sidebarListeners(){return this.enableNavigator?{"update:shownOnMobile":this.toggleMobileSidenav,"update:hiddenOnLarge":this.toggleLargeSidenav}:{}}},methods:{handleToggleSidenav(e){e===er.L3.large?this.toggleLargeSidenav():this.toggleMobileSidenav()},openQuickNavigationModal(){this.sidenavVisibleOnMobile||(this.showQuickNavigationModal=!0)},toggleLargeSidenav(e=!this.sidenavHiddenOnLarge){this.sidenavHiddenOnLarge=e,El.tO.set(Wu,e)},toggleMobileSidenav(e=!this.sidenavVisibleOnMobile){this.sidenavVisibleOnMobile=e},onQuickNavigationKeydown(e){("/"===e.key||"o"===e.key&&e.shiftKey&&e.metaKey)&&this.enableNavigator&&"input"!==e.target.tagName.toLowerCase()&&(this.openQuickNavigationModal(),e.preventDefault())}},mounted(){this.enableQuickNavigation&&window.addEventListener("keydown",this.onQuickNavigationKeydown)},beforeDestroy(){this.enableQuickNavigation&&window.removeEventListener("keydown",this.onQuickNavigationKeydown)},inject:{isTargetIDE:{default(){return!1}}}},Qu=Uu,Gu=(0,j.Z)(Qu,zr,Kr,!1,null,"8aa6db48",null),Xu=Gu.exports,Yu=n(2717);const Ju="symbol";var eh={watch:{topicData:{immediate:!0,handler:"extractOnThisPageSections"}},methods:{shouldRegisterContentSection(e){return e.type===ii.BlockType.heading&&e.level<4},extractOnThisPageSections(e){if(!e)return;this.store.resetPageSections();const{metadata:{title:t},primaryContentSections:n,topicSections:i,defaultImplementationsSections:s,relationshipsSections:a,seeAlsoSections:r,kind:o}=e;this.store.addOnThisPageSection({title:t,anchor:Yu.$,level:1,isSymbol:o===Ju},{i18n:!1}),n&&n.forEach((e=>{switch(e.kind){case je.mentions:this.store.addOnThisPageSection({title:this.$t("mentioned-in"),anchor:(0,hn.HA)("mentions"),level:2});break;case je.content:Ue.Z.methods.forEach.call(e,(e=>{this.shouldRegisterContentSection(e)&&this.store.addOnThisPageSection({title:e.text,anchor:e.anchor||(0,hn.HA)(e.text),level:e.level},{i18n:!1})}));break;case je.properties:case je.restBody:case je.restCookies:case je.restEndpoint:case je.restHeaders:case je.restParameters:case je.restResponses:this.store.addOnThisPageSection({title:e.title,anchor:(0,hn.HA)(e.title),level:2});break;default:Xt[e.kind]&&this.store.addOnThisPageSection(Xt[e.kind])}})),i&&this.store.addOnThisPageSection(Gt.topics),s&&this.store.addOnThisPageSection(Gt.defaultImplementations),a&&this.store.addOnThisPageSection(Gt.relationships),r&&this.store.addOnThisPageSection(Gt.seeAlso)}}},th=n(9030),nh=n(1944),ih=n(1789),sh=n(8093),ah=n(8571);const{extractProps:rh}=Mr.methods,oh="0.3.0";var lh={name:"DocumentationTopicView",constants:{MIN_RENDER_JSON_VERSION_WITH_INDEX:oh},components:{CodeTheme:sh.Z,Topic:Mr,DocumentationLayout:Xu},mixins:[eh,ih.Z],props:{enableMinimized:{type:Boolean,default:!1}},data(){return{topicDataDefault:null,topicDataObjc:null,store:rl.Z}},provide(){return{store:this.store}},computed:{disableHeroBackground:()=>!1,documentationLayoutProps:({topicProps:{diffAvailability:e,interfaceLanguage:t,references:n},enableNavigator:i,technology:s,parentTopicIdentifiers:a,objcPath:r,swiftPath:o,store:{state:{selectedAPIChangesVersion:l}}})=>({diffAvailability:e,interfaceLanguage:t,references:n,enableNavigator:i,technology:s,parentTopicIdentifiers:a,objcPath:r,swiftPath:o,selectedAPIChangesVersion:l}),objcOverrides:({topicData:e})=>{const{variantOverrides:t=[]}=e||{},n=({interfaceLanguage:e})=>e===D.Z.objectiveC.key.api,i=({traits:e})=>e.some(n),s=t.find(i);return s?s.patch:null},topicData:{get(){return this.topicDataObjc?this.topicDataObjc:this.topicDataDefault},set(e){this.topicDataDefault=e}},topicKey:({$route:e,topicProps:t})=>[e.path,t.interfaceLanguage].join(),topicProps(){return rh(this.topicData)},parentTopicIdentifiers:({topicProps:{hierarchy:e,references:t},$route:n})=>{if(!e)return[];const{paths:i=[]}=e;return i.length?i.find((e=>{const i=e.find((e=>t[e]&&"technologies"!==t[e].kind)),s=i&&t[i];return s&&n.path.toLowerCase().startsWith(s.url.toLowerCase())}))||i[0]:[]},technology:({$route:e,topicProps:{identifier:t,references:n,role:i,title:s},parentTopicIdentifiers:a})=>{const r={title:s,url:e.path},o=n[t];if(!a.length)return o||r;const l=n[a[0]];return l&&"technologies"!==l.kind?l:(i!==C.L.collection||o)&&(l&&n[a[1]]||o)||r},languagePaths:({topicData:{variants:e=[]}})=>e.reduce(((e,t)=>t.traits.reduce(((e,n)=>n.interfaceLanguage?{...e,[n.interfaceLanguage]:(e[n.interfaceLanguage]||[]).concat(t.paths)}:e),e)),{}),objcPath:({languagePaths:{[D.Z.objectiveC.key.api]:[e]=[]}={}})=>e,swiftPath:({languagePaths:{[D.Z.swift.key.api]:[e]=[]}={}})=>e,isSymbolBeta:({topicProps:{platforms:e}})=>!!(e&&e.length&&e.every((e=>e.beta))),isSymbolDeprecated:({topicProps:{platforms:e,deprecationSummary:t}})=>!!(t&&t.length>0||e&&e.length&&e.every((e=>e.deprecatedAt))),enableOnThisPageNav:({isTargetIDE:e})=>!(0,w.$8)(["features","docs","onThisPageNavigator","disable"],!1)&&!e,enableNavigator:({isTargetIDE:e,topicDataDefault:t})=>!e&&(0,nh.n4)((0,nh.W1)(t.schemaVersion),oh)>=0,rootHierarchyReference:({parentTopicIdentifiers:e,topicProps:{references:t}})=>t[e[0]]||{},isRootTechnologyLink:({rootHierarchyReference:{kind:e}})=>"technologies"===e,hierarchyItems:({parentTopicIdentifiers:e,isRootTechnologyLink:t})=>t?e.slice(1):e,rootLink:({isRootTechnologyLink:e,rootHierarchyReference:t,$route:n})=>e?{path:t.url,query:n.query}:null},methods:{handleCodeColorsChange(e){ah.Z.updateCodeColors(e)},applyObjcOverrides(){this.topicDataObjc=k((0,x.d9)(this.topicData),this.objcOverrides)}},mounted(){this.$bridge.on("contentUpdate",this.handleContentUpdateFromBridge),this.$bridge.on("codeColors",this.handleCodeColorsChange),this.$bridge.send({type:"requestCodeColors"})},beforeDestroy(){this.$bridge.off("contentUpdate",this.handleContentUpdateFromBridge),this.$bridge.off("codeColors",this.handleCodeColorsChange)},inject:{isTargetIDE:{default(){return!1}}},beforeRouteEnter(e,t,n){e.meta.skipFetchingData?n((e=>e.newContentMounted())):(0,x.Ek)(e,t,n).then((t=>n((n=>{(0,th.jk)(e.params.locale,n),n.topicData=t,e.query.language===D.Z.objectiveC.key.url&&n.objcOverrides&&n.applyObjcOverrides()})))).catch(n)},beforeRouteUpdate(e,t,n){e.path===t.path&&e.query.language===D.Z.objectiveC.key.url&&this.objcOverrides?(this.applyObjcOverrides(),n()):(0,x.Us)(e,t)?(0,x.Ek)(e,t,n).then((t=>{this.topicDataObjc=null,this.topicData=t,e.query.language===D.Z.objectiveC.key.url&&this.objcOverrides&&this.applyObjcOverrides(),(0,th.jk)(e.params.locale,this),n()})).catch(n):n()},created(){this.store.reset()},watch:{topicData(){this.$nextTick((()=>{this.newContentMounted()}))}}},ch=lh,dh=(0,j.Z)(ch,i,s,!1,null,null,null),uh=dh.exports},7274:function(e,t){var n,i,s;(function(a,r){i=[],n=r,s="function"===typeof n?n.apply(t,i):n,void 0===s||(e.exports=s)})(0,(function(){var e=/(auto|scroll)/,t=function(e,n){return null===e.parentNode?n:t(e.parentNode,n.concat([e]))},n=function(e,t){return getComputedStyle(e,null).getPropertyValue(t)},i=function(e){return n(e,"overflow")+n(e,"overflow-y")+n(e,"overflow-x")},s=function(t){return e.test(i(t))},a=function(e){if(e instanceof HTMLElement||e instanceof SVGElement){for(var n=t(e.parentNode,[]),i=0;i]+>",i="("+t+"|"+n.optional(a)+"[a-zA-Z_]\\w*"+n.optional(r)+")",l={className:"type",variants:[{begin:"\\b[a-z\\d_]*_t\\b"},{match:/\batomic_[a-z]{3,6}\b/}]},c="\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)",o={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'("+c+"|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},d={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},u={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(o,{className:"string"}),{className:"string",begin:/<.*?>/},s,e.C_BLOCK_COMMENT_MODE]},_={className:"title",begin:n.optional(a)+e.IDENT_RE,relevance:0},g=n.optional(a)+e.IDENT_RE+"\\s*\\(",p=["asm","auto","break","case","continue","default","do","else","enum","extern","for","fortran","goto","if","inline","register","restrict","return","sizeof","struct","switch","typedef","union","volatile","while","_Alignas","_Alignof","_Atomic","_Generic","_Noreturn","_Static_assert","_Thread_local","alignas","alignof","noreturn","static_assert","thread_local","_Pragma"],m=["float","double","signed","unsigned","int","short","long","char","void","_Bool","_Complex","_Imaginary","_Decimal32","_Decimal64","_Decimal128","const","static","complex","bool","imaginary"],f={keyword:p,type:m,literal:"true false NULL",built_in:"std string wstring cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set pair bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap priority_queue make_pair array shared_ptr abort terminate abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf future isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr"},b=[u,l,s,e.C_BLOCK_COMMENT_MODE,d,o],w={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:f,contains:b.concat([{begin:/\(/,end:/\)/,keywords:f,contains:b.concat(["self"]),relevance:0}]),relevance:0},y={begin:"("+i+"[\\*&\\s]+)+"+g,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:f,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:t,keywords:f,relevance:0},{begin:g,returnBegin:!0,contains:[e.inherit(_,{className:"title.function"})],relevance:0},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:f,relevance:0,contains:[s,e.C_BLOCK_COMMENT_MODE,o,d,l,{begin:/\(/,end:/\)/,keywords:f,relevance:0,contains:["self",s,e.C_BLOCK_COMMENT_MODE,o,d,l]}]},l,s,e.C_BLOCK_COMMENT_MODE,u]};return{name:"C",aliases:["h"],keywords:f,disableAutodetect:!0,illegal:"=]/,contains:[{beginKeywords:"final class struct"},e.TITLE_MODE]}]),exports:{preprocessor:u,strings:o,keywords:f}}}e.exports=n}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-cpp-js.458a9ae4.js b/docs/js/highlight-js-cpp-js.458a9ae4.js new file mode 100644 index 0000000..2996102 --- /dev/null +++ b/docs/js/highlight-js-cpp-js.458a9ae4.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[621],{6248:function(e){function t(e){const t=e.regex,n=e.COMMENT("//","$",{contains:[{begin:/\\\n/}]}),a="decltype\\(auto\\)",r="[a-zA-Z_]\\w*::",i="<[^<>]+>",s="(?!struct)("+a+"|"+t.optional(r)+"[a-zA-Z_]\\w*"+t.optional(i)+")",c={className:"type",begin:"\\b[a-z\\d_]*_t\\b"},o="\\\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4,8}|[0-7]{3}|\\S)",l={className:"string",variants:[{begin:'(u8?|U|L)?"',end:'"',illegal:"\\n",contains:[e.BACKSLASH_ESCAPE]},{begin:"(u8?|U|L)?'("+o+"|.)",end:"'",illegal:"."},e.END_SAME_AS_BEGIN({begin:/(?:u8?|U|L)?R"([^()\\ ]{0,16})\(/,end:/\)([^()\\ ]{0,16})"/})]},d={className:"number",variants:[{begin:"\\b(0b[01']+)"},{begin:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)((ll|LL|l|L)(u|U)?|(u|U)(ll|LL|l|L)?|f|F|b|B)"},{begin:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],relevance:0},u={className:"meta",begin:/#\s*[a-z]+\b/,end:/$/,keywords:{keyword:"if else elif endif define undef warning error line pragma _Pragma ifdef ifndef include"},contains:[{begin:/\\\n/,relevance:0},e.inherit(l,{className:"string"}),{className:"string",begin:/<.*?>/},n,e.C_BLOCK_COMMENT_MODE]},_={className:"title",begin:t.optional(r)+e.IDENT_RE,relevance:0},p=t.optional(r)+e.IDENT_RE+"\\s*\\(",m=["alignas","alignof","and","and_eq","asm","atomic_cancel","atomic_commit","atomic_noexcept","auto","bitand","bitor","break","case","catch","class","co_await","co_return","co_yield","compl","concept","const_cast|10","consteval","constexpr","constinit","continue","decltype","default","delete","do","dynamic_cast|10","else","enum","explicit","export","extern","false","final","for","friend","goto","if","import","inline","module","mutable","namespace","new","noexcept","not","not_eq","nullptr","operator","or","or_eq","override","private","protected","public","reflexpr","register","reinterpret_cast|10","requires","return","sizeof","static_assert","static_cast|10","struct","switch","synchronized","template","this","thread_local","throw","transaction_safe","transaction_safe_dynamic","true","try","typedef","typeid","typename","union","using","virtual","volatile","while","xor","xor_eq"],f=["bool","char","char16_t","char32_t","char8_t","double","float","int","long","short","void","wchar_t","unsigned","signed","const","static"],g=["any","auto_ptr","barrier","binary_semaphore","bitset","complex","condition_variable","condition_variable_any","counting_semaphore","deque","false_type","future","imaginary","initializer_list","istringstream","jthread","latch","lock_guard","multimap","multiset","mutex","optional","ostringstream","packaged_task","pair","promise","priority_queue","queue","recursive_mutex","recursive_timed_mutex","scoped_lock","set","shared_future","shared_lock","shared_mutex","shared_timed_mutex","shared_ptr","stack","string_view","stringstream","timed_mutex","thread","true_type","tuple","unique_lock","unique_ptr","unordered_map","unordered_multimap","unordered_multiset","unordered_set","variant","vector","weak_ptr","wstring","wstring_view"],b=["abort","abs","acos","apply","as_const","asin","atan","atan2","calloc","ceil","cerr","cin","clog","cos","cosh","cout","declval","endl","exchange","exit","exp","fabs","floor","fmod","forward","fprintf","fputs","free","frexp","fscanf","future","invoke","isalnum","isalpha","iscntrl","isdigit","isgraph","islower","isprint","ispunct","isspace","isupper","isxdigit","labs","launder","ldexp","log","log10","make_pair","make_shared","make_shared_for_overwrite","make_tuple","make_unique","malloc","memchr","memcmp","memcpy","memset","modf","move","pow","printf","putchar","puts","realloc","scanf","sin","sinh","snprintf","sprintf","sqrt","sscanf","std","stderr","stdin","stdout","strcat","strchr","strcmp","strcpy","strcspn","strlen","strncat","strncmp","strncpy","strpbrk","strrchr","strspn","strstr","swap","tan","tanh","terminate","to_underlying","tolower","toupper","vfprintf","visit","vprintf","vsprintf"],h=["NULL","false","nullopt","nullptr","true"],w=["_Pragma"],y={type:f,keyword:m,literal:h,built_in:w,_type_hints:g},v={className:"function.dispatch",relevance:0,keywords:{_hint:b},begin:t.concat(/\b/,/(?!decltype)/,/(?!if)/,/(?!for)/,/(?!switch)/,/(?!while)/,e.IDENT_RE,t.lookahead(/(<[^<>]+>|)\s*\(/))},k=[v,u,c,n,e.C_BLOCK_COMMENT_MODE,d,l],x={variants:[{begin:/=/,end:/;/},{begin:/\(/,end:/\)/},{beginKeywords:"new throw return else",end:/;/}],keywords:y,contains:k.concat([{begin:/\(/,end:/\)/,keywords:y,contains:k.concat(["self"]),relevance:0}]),relevance:0},E={className:"function",begin:"("+s+"[\\*&\\s]+)+"+p,returnBegin:!0,end:/[{;=]/,excludeEnd:!0,keywords:y,illegal:/[^\w\s\*&:<>.]/,contains:[{begin:a,keywords:y,relevance:0},{begin:p,returnBegin:!0,contains:[_],relevance:0},{begin:/::/,relevance:0},{begin:/:/,endsWithParent:!0,contains:[l,d]},{relevance:0,match:/,/},{className:"params",begin:/\(/,end:/\)/,keywords:y,relevance:0,contains:[n,e.C_BLOCK_COMMENT_MODE,l,d,c,{begin:/\(/,end:/\)/,keywords:y,relevance:0,contains:["self",n,e.C_BLOCK_COMMENT_MODE,l,d,c]}]},c,n,e.C_BLOCK_COMMENT_MODE,u]};return{name:"C++",aliases:["cc","c++","h++","hpp","hh","hxx","cxx"],keywords:y,illegal:"",keywords:y,contains:["self",c]},{begin:e.IDENT_RE+"::",keywords:y},{match:[/\b(?:enum(?:\s+(?:class|struct))?|class|struct|union)/,/\s+/,/\w+/],className:{1:"keyword",3:"title.class"}}])}}e.exports=t}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-css-js.bfc4251f.js b/docs/js/highlight-js-css-js.bfc4251f.js new file mode 100644 index 0000000..60ab9a7 --- /dev/null +++ b/docs/js/highlight-js-css-js.bfc4251f.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[864],{5064:function(e){const t=e=>({IMPORTANT:{scope:"meta",begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:"number",begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z][A-Za-z0-9_-]*/}}),o=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],i=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],r=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],a=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],n=["align-content","align-items","align-self","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","isolation","justify-content","left","letter-spacing","line-break","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-height","max-width","min-height","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","resize","rest","rest-after","rest-before","right","row-gap","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","shape-image-threshold","shape-margin","shape-outside","speak","speak-as","src","tab-size","table-layout","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","z-index"].reverse();function l(e){const l=e.regex,s=t(e),d={begin:/-(webkit|moz|ms|o)-(?=[a-z])/},c="and or not only",g=/@-?\w[\w]*(-\w+)*/,m="[a-zA-Z-][a-zA-Z0-9_-]*",p=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE];return{name:"CSS",case_insensitive:!0,illegal:/[=|'\$]/,keywords:{keyframePosition:"from to"},classNameAliases:{keyframePosition:"selector-tag"},contains:[s.BLOCK_COMMENT,d,s.CSS_NUMBER_MODE,{className:"selector-id",begin:/#[A-Za-z0-9_-]+/,relevance:0},{className:"selector-class",begin:"\\."+m,relevance:0},s.ATTRIBUTE_SELECTOR_MODE,{className:"selector-pseudo",variants:[{begin:":("+r.join("|")+")"},{begin:":(:)?("+a.join("|")+")"}]},s.CSS_VARIABLE,{className:"attribute",begin:"\\b("+n.join("|")+")\\b"},{begin:/:/,end:/[;}{]/,contains:[s.BLOCK_COMMENT,s.HEXCOLOR,s.IMPORTANT,s.CSS_NUMBER_MODE,...p,{begin:/(url|data-uri)\(/,end:/\)/,relevance:0,keywords:{built_in:"url data-uri"},contains:[{className:"string",begin:/[^)]/,endsWithParent:!0,excludeEnd:!0}]},s.FUNCTION_DISPATCH]},{begin:l.lookahead(/@/),end:"[{;]",relevance:0,illegal:/:/,contains:[{className:"keyword",begin:g},{begin:/\s/,endsWithParent:!0,excludeEnd:!0,relevance:0,keywords:{$pattern:/[a-z-]+/,keyword:c,attribute:i.join(" ")},contains:[{begin:/[a-z-]+(?=:)/,className:"attribute"},...p,s.CSS_NUMBER_MODE]}]},{className:"selector-tag",begin:"\\b("+o.join("|")+")\\b"}]}}e.exports=l}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-custom-markdown.78c9f6ed.js b/docs/js/highlight-js-custom-markdown.78c9f6ed.js new file mode 100644 index 0000000..9a52dcd --- /dev/null +++ b/docs/js/highlight-js-custom-markdown.78c9f6ed.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +"use strict";(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[642],{2003:function(e,n,a){function i(e){const n=e.regex,a={begin:/<\/?[A-Za-z_]/,end:">",subLanguage:"xml",relevance:0},i={begin:"^[-\\*]{3,}",end:"$"},s={className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},c={className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},t={begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]},d=/[A-Za-z][A-Za-z0-9+.-]*/,l={variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0},{begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,relevance:2},{begin:n.concat(/\[.+?\]\(/,d,/:\/\/.*?\)/),relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}]},g={className:"strong",contains:[],variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},r={className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{begin:/_(?!_)/,end:/_/,relevance:0}]};g.contains.push(r),r.contains.push(g);let o=[a,l];g.contains=g.contains.concat(o),r.contains=r.contains.concat(o),o=o.concat(g,r);const b={className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:o},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:o}]}]},u={className:"quote",begin:"^>\\s+",contains:o,end:"$"};return{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[b,a,c,g,r,u,s,i,l,t]}}a.r(n),a.d(n,{default:function(){return l}});const s={begin:"",returnBegin:!0,contains:[{className:"link",begin:"doc:",end:">",excludeEnd:!0}]},c={className:"link",begin:/`{2}(?!`)/,end:/`{2}(?!`)/,excludeBegin:!0,excludeEnd:!0},t={begin:"^>\\s+[Note:|Tip:|Important:|Experiment:|Warning:]",end:"$",returnBegin:!0,contains:[{className:"quote",begin:"^>",end:"\\s+"},{className:"type",begin:"Note|Tip|Important|Experiment|Warning",end:":"},{className:"quote",begin:".*",end:"$",endsParent:!0}]},d={begin:"@",end:"[{\\)\\s]",returnBegin:!0,contains:[{className:"title",begin:"@",end:"[\\s+(]",excludeEnd:!0},{begin:":",end:"[,\\)\n\t]",excludeBegin:!0,keywords:{literal:"true false null undefined"},contains:[{className:"number",begin:"\\b([\\d_]+(\\.[\\deE_]+)?|0x[a-fA-F0-9_]+(\\.[a-fA-F0-9p_]+)?|0b[01_]+|0o[0-7_]+)\\b",endsWithParent:!0,excludeEnd:!0},{className:"string",variants:[{begin:/"""/,end:/"""/},{begin:/"/,end:/"/}],endsParent:!0},{className:"link",begin:"http|https",endsWithParent:!0,excludeEnd:!0}]}]};function l(e){const n=i(e),a=n.contains.find((({className:e})=>"code"===e));a.variants=a.variants.filter((({begin:e})=>!e.includes("( {4}|\\t)")));const l=[...n.contains.filter((({className:e})=>"code"!==e)),a];return{...n,contains:[c,s,t,d,...l]}}}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-custom-swift.738731d1.js b/docs/js/highlight-js-custom-swift.738731d1.js new file mode 100644 index 0000000..a6f83da --- /dev/null +++ b/docs/js/highlight-js-custom-swift.738731d1.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +"use strict";(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[217],{7467:function(e,n,t){function a(e){return e?"string"===typeof e?e:e.source:null}function s(e){return i("(?=",e,")")}function i(...e){const n=e.map((e=>a(e))).join("");return n}function c(e){const n=e[e.length-1];return"object"===typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{}}function u(...e){const n=c(e),t="("+(n.capture?"":"?:")+e.map((e=>a(e))).join("|")+")";return t}t.r(n),t.d(n,{default:function(){return D}});const o=e=>i(/\b/,e,/\w$/.test(e)?/\b/:/\B/),r=["Protocol","Type"].map(o),l=["init","self"].map(o),d=["Any","Self"],m=["actor","associatedtype","async","await",/as\?/,/as!/,"as","break","case","catch","class","continue","convenience","default","defer","deinit","didSet","do","dynamic","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","isolated","nonisolated","lazy","let","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],p=["false","nil","true"],b=["assignment","associativity","higherThan","left","lowerThan","none","right"],F=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warn_unqualified_access","#warning"],f=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],h=u(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),w=u(h,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),y=i(h,w,"*"),g=u(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),E=u(g,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),v=i(g,E,"*"),N=i(/[A-Z]/,E,"*"),k=["autoclosure",i(/convention\(/,u("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",i(/objc\(/,v,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","testable","UIApplicationMain","unknown","usableFromInline"],A=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"];function C(e){const n={match:/\s+/,relevance:0},t=e.COMMENT("/\\*","\\*/",{contains:["self"]}),a=[e.C_LINE_COMMENT_MODE,t],c={match:[/\./,u(...r,...l)],className:{2:"keyword"}},h={match:i(/\./,u(...m)),relevance:0},g=m.filter((e=>"string"===typeof e)).concat(["_|0"]),C=m.filter((e=>"string"!==typeof e)).concat(d).map(o),D={variants:[{className:"keyword",match:u(...C,...l)}]},B={$pattern:u(/\b\w+/,/#\w+/),keyword:g.concat(F),literal:p},_=[c,h,D],S={match:i(/\./,u(...f)),relevance:0},x={className:"built_in",match:i(/\b/,u(...f),/(?=\()/)},M=[S,x],I={match:/->/,relevance:0},$={className:"operator",relevance:0,variants:[{match:y},{match:`\\.(\\.|${w})+`}]},O=[I,$],L="([0-9]_*)+",T="([0-9a-fA-F]_*)+",K={className:"number",relevance:0,variants:[{match:`\\b(${L})(\\.(${L}))?([eE][+-]?(${L}))?\\b`},{match:`\\b0x(${T})(\\.(${T}))?([pP][+-]?(${L}))?\\b`},{match:/\b0o([0-7]_*)+\b/},{match:/\b0b([01]_*)+\b/}]},P=(e="")=>({className:"subst",variants:[{match:i(/\\/,e,/[0\\tnr"']/)},{match:i(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}]}),j=(e="")=>({className:"subst",match:i(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/)}),z=(e="")=>({className:"subst",label:"interpol",begin:i(/\\/,e,/\(/),end:/\)/}),q=(e="")=>({begin:i(e,/"""/),end:i(/"""/,e),contains:[P(e),j(e),z(e)]}),U=(e="")=>({begin:i(e,/"/),end:i(/"/,e),contains:[P(e),z(e)]}),W={className:"string",variants:[q(),q("#"),q("##"),q("###"),U(),U("#"),U("##"),U("###")]},Z={match:i(/`/,v,/`/)},V={className:"variable",match:/\$\d+/},G={className:"variable",match:`\\$${E}+`},R=[Z,V,G],X={match:/(@|#)available/,className:"keyword",starts:{contains:[{begin:/\(/,end:/\)/,keywords:A,contains:[...O,K,W]}]}},H={className:"keyword",match:i(/@/,u(...k))},J={className:"meta",match:i(/@/,v)},Q=[X,H,J],Y={match:s(/\b[A-Z]/),relevance:0,contains:[{className:"type",match:i(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,E,"+")},{className:"type",match:N,relevance:0},{match:/[?!]+/,relevance:0},{match:/\.\.\./,relevance:0},{match:i(/\s+&\s+/,s(N)),relevance:0}]},ee={begin://,keywords:B,contains:[...a,..._,...Q,I,Y]};Y.contains.push(ee);const ne={match:i(v,/\s*:/),keywords:"_|0",relevance:0},te={begin:/\(/,end:/\)/,relevance:0,keywords:B,contains:["self",ne,...a,..._,...M,...O,K,W,...R,...Q,Y]},ae={begin://,contains:[...a,Y]},se={begin:u(s(i(v,/\s*:/)),s(i(v,/\s+/,v,/\s*:/))),end:/:/,relevance:0,contains:[{className:"keyword",match:/\b_\b/},{className:"params",match:v}]},ie={begin:/\(/,end:/\)/,keywords:B,contains:[se,...a,..._,...O,K,W,...Q,Y,te],endsParent:!0,illegal:/["']/},ce={match:[/func/,/\s+/,u(Z.match,v,y)],className:{1:"keyword",3:"title.function"},contains:[ae,ie,n],illegal:[/\[/,/%/]},ue={match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"},contains:[ae,ie,n],illegal:/\[|%/},oe={match:[/operator/,/\s+/,y],className:{1:"keyword",3:"title"}},re={begin:[/precedencegroup/,/\s+/,N],className:{1:"keyword",3:"title"},contains:[Y],keywords:[...b,...p],end:/}/};for(const s of W.variants){const e=s.contains.find((e=>"interpol"===e.label));e.keywords=B;const n=[..._,...M,...O,K,W,...R];e.contains=[...n,{begin:/\(/,end:/\)/,contains:["self",...n]}]}return{name:"Swift",keywords:B,contains:[...a,ce,ue,{beginKeywords:"struct protocol class extension enum actor",end:"\\{",excludeEnd:!0,keywords:B,contains:[e.inherit(e.TITLE_MODE,{className:"title.class",begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),..._]},oe,re,{beginKeywords:"import",end:/$/,contains:[...a],relevance:0},..._,...M,...O,K,W,...R,...Q,Y,te]}}function D(e){const n=C(e);n.keywords.keyword=[...n.keywords.keyword,"distributed"];const t=({beginKeywords:e=""})=>e.split(" ").includes("class"),a=n.contains.findIndex(t);if(a>=0){const{beginKeywords:e,...t}=n.contains[a];n.contains[a]={...t,begin:/\b(struct|protocol|extension|enum|actor|class\b(?!.*\bfunc))\b/}}const s=e=>{const{className:n,match:t}=e;if("subst"!==n||!t)return!1;const a=t.toString();return a.startsWith("\\")&&a.endsWith("[\\t ]*(?:[\\r\\n]|\\r\\n)")},i=n.contains.findIndex((({className:e})=>"string"===e));return n.contains[i]={...n.contains[i],variants:n.contains[i].variants.map((e=>({...e,contains:e.contains.map((e=>s(e)?{className:"subst",begin:/\\#{0,3}/,end:/[\t ]*(?:[\r\n]|\r\n)/,excludeEnd:!0}:e))})))},n}}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-diff-js.4db9a783.js b/docs/js/highlight-js-diff-js.4db9a783.js new file mode 100644 index 0000000..d0f5a78 --- /dev/null +++ b/docs/js/highlight-js-diff-js.4db9a783.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[213],{7731:function(e){function n(e){const n=e.regex;return{name:"Diff",aliases:["patch"],contains:[{className:"meta",relevance:10,match:n.either(/^@@ +-\d+,\d+ +\+\d+,\d+ +@@/,/^\*\*\* +\d+,\d+ +\*\*\*\*$/,/^--- +\d+,\d+ +----$/)},{className:"comment",variants:[{begin:n.either(/Index: /,/^index/,/={3,}/,/^-{3}/,/^\*{3} /,/^\+{3}/,/^diff --git/),end:/$/},{match:/^\*{15}$/}]},{className:"addition",begin:/^\+/,end:/$/},{className:"deletion",begin:/^-/,end:/$/},{className:"addition",begin:/^!/,end:/$/}]}}e.exports=n}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-http-js.f78e83c2.js b/docs/js/highlight-js-http-js.f78e83c2.js new file mode 100644 index 0000000..a400cae --- /dev/null +++ b/docs/js/highlight-js-http-js.f78e83c2.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[878],{8937:function(e){function n(e){const n=e.regex,a="HTTP/(2|1\\.[01])",s=/[A-Za-z][A-Za-z0-9-]*/,t={className:"attribute",begin:n.concat("^",s,"(?=\\:\\s)"),starts:{contains:[{className:"punctuation",begin:/: /,relevance:0,starts:{end:"$",relevance:0}}]}},i=[t,{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}];return{name:"HTTP",aliases:["https"],illegal:/\S/,contains:[{begin:"^(?="+a+" \\d{3})",end:/$/,contains:[{className:"meta",begin:a},{className:"number",begin:"\\b\\d{3}\\b"}],starts:{end:/\b\B/,illegal:/\S/,contains:i}},{begin:"(?=^[A-Z]+ (.*?) "+a+"$)",end:/$/,contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{className:"meta",begin:a},{className:"keyword",begin:"[A-Z]+"}],starts:{end:/\b\B/,illegal:/\S/,contains:i}},e.inherit(t,{relevance:0})]}}e.exports=n}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-java-js.4fe21e94.js b/docs/js/highlight-js-java-js.4fe21e94.js new file mode 100644 index 0000000..5062f61 --- /dev/null +++ b/docs/js/highlight-js-java-js.4fe21e94.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[788],{8257:function(e){var n="[0-9](_*[0-9])*",a=`\\.(${n})`,s="[0-9a-fA-F](_*[0-9a-fA-F])*",t={className:"number",variants:[{begin:`(\\b(${n})((${a})|\\.)?|(${a}))[eE][+-]?(${n})[fFdD]?\\b`},{begin:`\\b(${n})((${a})[fFdD]?\\b|\\.([fFdD]\\b)?)`},{begin:`(${a})[fFdD]?\\b`},{begin:`\\b(${n})[fFdD]\\b`},{begin:`\\b0[xX]((${s})\\.?|(${s})?\\.(${s}))[pP][+-]?(${n})[fFdD]?\\b`},{begin:"\\b(0|[1-9](_*[0-9])*)[lL]?\\b"},{begin:`\\b0[xX](${s})[lL]?\\b`},{begin:"\\b0(_*[0-7])*[lL]?\\b"},{begin:"\\b0[bB][01](_*[01])*[lL]?\\b"}],relevance:0};function i(e,n,a){return-1===a?"":e.replace(n,(s=>i(e,n,a-1)))}function r(e){e.regex;const n="[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",a=n+i("(?:<"+n+"~~~(?:\\s*,\\s*"+n+"~~~)*>)?",/~~~/g,2),s=["synchronized","abstract","private","var","static","if","const ","for","while","strictfp","finally","protected","import","native","final","void","enum","else","break","transient","catch","instanceof","volatile","case","assert","package","default","public","try","switch","continue","throws","protected","public","private","module","requires","exports","do"],r=["super","this"],c=["false","true","null"],l=["char","boolean","long","float","int","byte","short","double"],b={keyword:s,literal:c,type:l,built_in:r},o={className:"meta",begin:"@"+n,contains:[{begin:/\(/,end:/\)/,contains:["self"]}]},_={className:"params",begin:/\(/,end:/\)/,keywords:b,relevance:0,contains:[e.C_BLOCK_COMMENT_MODE],endsParent:!0};return{name:"Java",aliases:["jsp"],keywords:b,illegal:/<\/|#/,contains:[e.COMMENT("/\\*\\*","\\*/",{relevance:0,contains:[{begin:/\w+@/,relevance:0},{className:"doctag",begin:"@[A-Za-z]+"}]}),{begin:/import java\.[a-z]+\./,keywords:"import",relevance:2},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,{begin:/"""/,end:/"""/,className:"string",contains:[e.BACKSLASH_ESCAPE]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,{match:[/\b(?:class|interface|enum|extends|implements|new)/,/\s+/,n],className:{1:"keyword",3:"title.class"}},{begin:[n,/\s+/,n,/\s+/,/=/],className:{1:"type",3:"variable",5:"operator"}},{begin:[/record/,/\s+/,n],className:{1:"keyword",3:"title.class"},contains:[_,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{beginKeywords:"new throw return else",relevance:0},{begin:["(?:"+a+"\\s+)",e.UNDERSCORE_IDENT_RE,/\s*(?=\()/],className:{2:"title.function"},keywords:b,contains:[{className:"params",begin:/\(/,end:/\)/,keywords:b,relevance:0,contains:[o,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,t,e.C_BLOCK_COMMENT_MODE]},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},t,o]}}e.exports=r}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-javascript-js.dfc9d16d.js b/docs/js/highlight-js-javascript-js.dfc9d16d.js new file mode 100644 index 0000000..d38ee05 --- /dev/null +++ b/docs/js/highlight-js-javascript-js.dfc9d16d.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[814],{978:function(e){const n="[A-Za-z$_][0-9A-Za-z$_]*",a=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],t=["true","false","null","undefined","NaN","Infinity"],s=["Object","Function","Boolean","Symbol","Math","Date","Number","BigInt","String","RegExp","Array","Float32Array","Float64Array","Int8Array","Uint8Array","Uint8ClampedArray","Int16Array","Int32Array","Uint16Array","Uint32Array","BigInt64Array","BigUint64Array","Set","Map","WeakSet","WeakMap","ArrayBuffer","SharedArrayBuffer","Atomics","DataView","JSON","Promise","Generator","GeneratorFunction","AsyncFunction","Reflect","Proxy","Intl","WebAssembly"],c=["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"],r=["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],i=["arguments","this","super","console","window","document","localStorage","module","global"],o=[].concat(r,s,c);function l(e){const l=e.regex,b=(e,{after:n})=>{const a="",end:""},u=/<[A-Za-z0-9\\._:-]+\s*\/>/,m={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,n)=>{const a=e[0].length+e.index,t=e.input[a];if("<"===t||","===t)return void n.ignoreMatch();let s;">"===t&&(b(e,{after:a})||n.ignoreMatch());const c=e.input.substr(a);(s=c.match(/^\s+extends\s+/))&&0===s.index&&n.ignoreMatch()}},E={$pattern:n,keyword:a,literal:t,built_in:o,"variable.language":i},A="[0-9](_?[0-9])*",f=`\\.(${A})`,_="0|[1-9](_?[0-9])*|0[0-7]*[89][0-9]*",y={className:"number",variants:[{begin:`(\\b(${_})((${f})|\\.)?|(${f}))[eE][+-]?(${A})\\b`},{begin:`\\b(${_})\\b((${f})\\b|\\.)?|(${f})\\b`},{begin:"\\b(0|[1-9](_?[0-9])*)n\\b"},{begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*n?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*n?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*n?\\b"},{begin:"\\b0[0-7]+n?\\b"}],relevance:0},N={className:"subst",begin:"\\$\\{",end:"\\}",keywords:E,contains:[]},h={begin:"html`",end:"",starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,N],subLanguage:"xml"}},p={begin:"css`",end:"",starts:{end:"`",returnEnd:!1,contains:[e.BACKSLASH_ESCAPE,N],subLanguage:"css"}},v={className:"string",begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE,N]},w=e.COMMENT(/\/\*\*(?!\/)/,"\\*/",{relevance:0,contains:[{begin:"(?=@[A-Za-z]+)",relevance:0,contains:[{className:"doctag",begin:"@[A-Za-z]+"},{className:"type",begin:"\\{",end:"\\}",excludeEnd:!0,excludeBegin:!0,relevance:0},{className:"variable",begin:d+"(?=\\s*(-)|$)",endsParent:!0,relevance:0},{begin:/(?=[^\n])\s/,relevance:0}]}]}),S={className:"comment",variants:[w,e.C_BLOCK_COMMENT_MODE,e.C_LINE_COMMENT_MODE]},R=[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,h,p,v,y];N.contains=R.concat({begin:/\{/,end:/\}/,keywords:E,contains:["self"].concat(R)});const k=[].concat(S,N.contains),O=k.concat([{begin:/\(/,end:/\)/,keywords:E,contains:["self"].concat(k)}]),I={className:"params",begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:E,contains:O},x={variants:[{match:[/class/,/\s+/,d,/\s+/,/extends/,/\s+/,l.concat(d,"(",l.concat(/\./,d),")*")],scope:{1:"keyword",3:"title.class",5:"keyword",7:"title.class.inherited"}},{match:[/class/,/\s+/,d],scope:{1:"keyword",3:"title.class"}}]},T={relevance:0,match:l.either(/\bJSON/,/\b[A-Z][a-z]+([A-Z][a-z]+|\d)*/,/\b[A-Z]{2,}([A-Z][a-z]+|\d)+/),className:"title.class",keywords:{_:[...s,...c]}},C={label:"use_strict",className:"meta",relevance:10,begin:/^\s*['"]use (strict|asm)['"]/},M={variants:[{match:[/function/,/\s+/,d,/(?=\s*\()/]},{match:[/function/,/\s*(?=\()/]}],className:{1:"keyword",3:"title.function"},label:"func.def",contains:[I],illegal:/%/},B={relevance:0,match:/\b[A-Z][A-Z_0-9]+\b/,className:"variable.constant"};function $(e){return l.concat("(?!",e.join("|"),")")}const D={match:l.concat(/\b/,$([...r,"super"]),d,l.lookahead(/\(/)),className:"title.function",relevance:0},U={begin:l.concat(/\./,l.lookahead(l.concat(d,/(?![0-9A-Za-z$_(])/))),end:d,excludeBegin:!0,keywords:"prototype",className:"property",relevance:0},Z={match:[/get|set/,/\s+/,d,/(?=\()/],className:{1:"keyword",3:"title.function"},contains:[{begin:/\(\)/},I]},z="(\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)|"+e.UNDERSCORE_IDENT_RE+")\\s*=>",F={match:[/const|var|let/,/\s+/,d,/\s*/,/=\s*/,l.lookahead(z)],className:{1:"keyword",3:"title.function"},contains:[I]};return{name:"Javascript",aliases:["js","jsx","mjs","cjs"],keywords:E,exports:{PARAMS_CONTAINS:O,CLASS_REFERENCE:T},illegal:/#(?![$_A-z])/,contains:[e.SHEBANG({label:"shebang",binary:"node",relevance:5}),C,e.APOS_STRING_MODE,e.QUOTE_STRING_MODE,h,p,v,S,y,T,{className:"attr",begin:d+l.lookahead(":"),relevance:0},F,{begin:"("+e.RE_STARTERS_RE+"|\\b(case|return|throw)\\b)\\s*",keywords:"return throw case",relevance:0,contains:[S,e.REGEXP_MODE,{className:"function",begin:z,returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:E,contains:O}]}]},{begin:/,/,relevance:0},{match:/\s+/,relevance:0},{variants:[{begin:g.begin,end:g.end},{match:u},{begin:m.begin,"on:begin":m.isTrulyOpeningTag,end:m.end}],subLanguage:"xml",contains:[{begin:m.begin,end:m.end,skip:!0,contains:["self"]}]}]},M,{beginKeywords:"while if switch catch for"},{begin:"\\b(?!function)"+e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,label:"func.def",contains:[I,e.inherit(e.TITLE_MODE,{begin:d,className:"title.function"})]},{match:/\.\.\./,relevance:0},U,{match:"\\$"+d,relevance:0},{match:[/\bconstructor(?=\s*\()/],className:{1:"title.function"},contains:[I]},D,B,x,Z,{match:/\$[(.]/}]}}e.exports=l}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-json-js.2a1856ba.js b/docs/js/highlight-js-json-js.2a1856ba.js new file mode 100644 index 0000000..aa882db --- /dev/null +++ b/docs/js/highlight-js-json-js.2a1856ba.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[82],{14:function(e){function n(e){const n={className:"attr",begin:/"(\\.|[^\\"\r\n])*"(?=\s*:)/,relevance:1.01},c={match:/[{}[\],:]/,className:"punctuation",relevance:0},a={beginKeywords:["true","false","null"].join(" ")};return{name:"JSON",contains:[n,c,e.QUOTE_STRING_MODE,a,e.C_NUMBER_MODE,e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],illegal:"\\S"}}e.exports=n}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-llvm-js.26121771.js b/docs/js/highlight-js-llvm-js.26121771.js new file mode 100644 index 0000000..74730ea --- /dev/null +++ b/docs/js/highlight-js-llvm-js.26121771.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[133],{4972:function(e){function n(e){const n=e.regex,a=/([-a-zA-Z$._][\w$.-]*)/,t={className:"type",begin:/\bi\d+(?=\s|\b)/},i={className:"operator",relevance:0,begin:/=/},c={className:"punctuation",relevance:0,begin:/,/},l={className:"number",variants:[{begin:/0[xX][a-fA-F0-9]+/},{begin:/-?\d+(?:[.]\d+)?(?:[eE][-+]?\d+(?:[.]\d+)?)?/}],relevance:0},r={className:"symbol",variants:[{begin:/^\s*[a-z]+:/}],relevance:0},s={className:"variable",variants:[{begin:n.concat(/%/,a)},{begin:/%\d+/},{begin:/#\d+/}]},o={className:"title",variants:[{begin:n.concat(/@/,a)},{begin:/@\d+/},{begin:n.concat(/!/,a)},{begin:n.concat(/!\d+/,a)},{begin:/!\d+/}]};return{name:"LLVM IR",keywords:"begin end true false declare define global constant private linker_private internal available_externally linkonce linkonce_odr weak weak_odr appending dllimport dllexport common default hidden protected extern_weak external thread_local zeroinitializer undef null to tail target triple datalayout volatile nuw nsw nnan ninf nsz arcp fast exact inbounds align addrspace section alias module asm sideeffect gc dbg linker_private_weak attributes blockaddress initialexec localdynamic localexec prefix unnamed_addr ccc fastcc coldcc x86_stdcallcc x86_fastcallcc arm_apcscc arm_aapcscc arm_aapcs_vfpcc ptx_device ptx_kernel intel_ocl_bicc msp430_intrcc spir_func spir_kernel x86_64_sysvcc x86_64_win64cc x86_thiscallcc cc c signext zeroext inreg sret nounwind noreturn noalias nocapture byval nest readnone readonly inlinehint noinline alwaysinline optsize ssp sspreq noredzone noimplicitfloat naked builtin cold nobuiltin noduplicate nonlazybind optnone returns_twice sanitize_address sanitize_memory sanitize_thread sspstrong uwtable returned type opaque eq ne slt sgt sle sge ult ugt ule uge oeq one olt ogt ole oge ord uno ueq une x acq_rel acquire alignstack atomic catch cleanup filter inteldialect max min monotonic nand personality release seq_cst singlethread umax umin unordered xchg add fadd sub fsub mul fmul udiv sdiv fdiv urem srem frem shl lshr ashr and or xor icmp fcmp phi call trunc zext sext fptrunc fpext uitofp sitofp fptoui fptosi inttoptr ptrtoint bitcast addrspacecast select va_arg ret br switch invoke unwind unreachable indirectbr landingpad resume malloc alloca free load store getelementptr extractelement insertelement shufflevector getresult extractvalue insertvalue atomicrmw cmpxchg fence argmemonly double",contains:[t,e.COMMENT(/;\s*$/,null,{relevance:0}),e.COMMENT(/;/,/$/),e.QUOTE_STRING_MODE,{className:"string",variants:[{begin:/"/,end:/[^\\]"/}]},o,c,i,s,r,l]}}e.exports=n}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-markdown-js.a2f456af.js b/docs/js/highlight-js-markdown-js.a2f456af.js new file mode 100644 index 0000000..c9c3762 --- /dev/null +++ b/docs/js/highlight-js-markdown-js.a2f456af.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[113],{1312:function(e){function n(e){const n=e.regex,a={begin:/<\/?[A-Za-z_]/,end:">",subLanguage:"xml",relevance:0},i={begin:"^[-\\*]{3,}",end:"$"},c={className:"code",variants:[{begin:"(`{3,})[^`](.|\\n)*?\\1`*[ ]*"},{begin:"(~{3,})[^~](.|\\n)*?\\1~*[ ]*"},{begin:"```",end:"```+[ ]*$"},{begin:"~~~",end:"~~~+[ ]*$"},{begin:"`.+?`"},{begin:"(?=^( {4}|\\t))",contains:[{begin:"^( {4}|\\t)",end:"(\\n)$"}],relevance:0}]},s={className:"bullet",begin:"^[ \t]*([*+-]|(\\d+\\.))(?=\\s+)",end:"\\s+",excludeEnd:!0},t={begin:/^\[[^\n]+\]:/,returnBegin:!0,contains:[{className:"symbol",begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0},{className:"link",begin:/:\s*/,end:/$/,excludeBegin:!0}]},d=/[A-Za-z][A-Za-z0-9+.-]*/,l={variants:[{begin:/\[.+?\]\[.*?\]/,relevance:0},{begin:/\[.+?\]\(((data|javascript|mailto):|(?:http|ftp)s?:\/\/).*?\)/,relevance:2},{begin:n.concat(/\[.+?\]\(/,d,/:\/\/.*?\)/),relevance:2},{begin:/\[.+?\]\([./?&#].*?\)/,relevance:1},{begin:/\[.*?\]\(.*?\)/,relevance:0}],returnBegin:!0,contains:[{match:/\[(?=\])/},{className:"string",relevance:0,begin:"\\[",end:"\\]",excludeBegin:!0,returnEnd:!0},{className:"link",relevance:0,begin:"\\]\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0},{className:"symbol",relevance:0,begin:"\\]\\[",end:"\\]",excludeBegin:!0,excludeEnd:!0}]},g={className:"strong",contains:[],variants:[{begin:/_{2}/,end:/_{2}/},{begin:/\*{2}/,end:/\*{2}/}]},b={className:"emphasis",contains:[],variants:[{begin:/\*(?!\*)/,end:/\*/},{begin:/_(?!_)/,end:/_/,relevance:0}]};g.contains.push(b),b.contains.push(g);let o=[a,l];g.contains=g.contains.concat(o),b.contains=b.contains.concat(o),o=o.concat(g,b);const r={className:"section",variants:[{begin:"^#{1,6}",end:"$",contains:o},{begin:"(?=^.+?\\n[=-]{2,}$)",contains:[{begin:"^[=-]*$"},{begin:"^",end:"\\n",contains:o}]}]},u={className:"quote",begin:"^>\\s+",contains:o,end:"$"};return{name:"Markdown",aliases:["md","mkdown","mkd"],contains:[r,a,s,g,b,u,c,i,l,t]}}e.exports=n}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-objectivec-js.74dea052.js b/docs/js/highlight-js-objectivec-js.74dea052.js new file mode 100644 index 0000000..d26ddf6 --- /dev/null +++ b/docs/js/highlight-js-objectivec-js.74dea052.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[637],{2446:function(e){function n(e){const n={className:"built_in",begin:"\\b(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)\\w+"},_=/[a-zA-Z@][a-zA-Z0-9_]*/,i=["int","float","while","char","export","sizeof","typedef","const","struct","for","union","unsigned","long","volatile","static","bool","mutable","if","do","return","goto","void","enum","else","break","extern","asm","case","short","default","double","register","explicit","signed","typename","this","switch","continue","wchar_t","inline","readonly","assign","readwrite","self","@synchronized","id","typeof","nonatomic","super","unichar","IBOutlet","IBAction","strong","weak","copy","in","out","inout","bycopy","byref","oneway","__strong","__weak","__block","__autoreleasing","@private","@protected","@public","@try","@property","@end","@throw","@catch","@finally","@autoreleasepool","@synthesize","@dynamic","@selector","@optional","@required","@encode","@package","@import","@defs","@compatibility_alias","__bridge","__bridge_transfer","__bridge_retained","__bridge_retain","__covariant","__contravariant","__kindof","_Nonnull","_Nullable","_Null_unspecified","__FUNCTION__","__PRETTY_FUNCTION__","__attribute__","getter","setter","retain","unsafe_unretained","nonnull","nullable","null_unspecified","null_resettable","class","instancetype","NS_DESIGNATED_INITIALIZER","NS_UNAVAILABLE","NS_REQUIRES_SUPER","NS_RETURNS_INNER_POINTER","NS_INLINE","NS_AVAILABLE","NS_DEPRECATED","NS_ENUM","NS_OPTIONS","NS_SWIFT_UNAVAILABLE","NS_ASSUME_NONNULL_BEGIN","NS_ASSUME_NONNULL_END","NS_REFINED_FOR_SWIFT","NS_SWIFT_NAME","NS_SWIFT_NOTHROW","NS_DURING","NS_HANDLER","NS_ENDHANDLER","NS_VALUERETURN","NS_VOIDRETURN"],t=["false","true","FALSE","TRUE","nil","YES","NO","NULL"],a=["BOOL","dispatch_once_t","dispatch_queue_t","dispatch_sync","dispatch_async","dispatch_once"],r={$pattern:_,keyword:i,literal:t,built_in:a},s={$pattern:_,keyword:["@interface","@class","@protocol","@implementation"]};return{name:"Objective-C",aliases:["mm","objc","obj-c","obj-c++","objective-c++"],keywords:r,illegal:"/,end:/$/,illegal:"\\n"},e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE]},{className:"class",begin:"("+s.keyword.join("|")+")\\b",end:/(\{|$)/,excludeEnd:!0,keywords:s,contains:[e.UNDERSCORE_TITLE_MODE]},{begin:"\\."+e.UNDERSCORE_IDENT_RE,relevance:0}]}}e.exports=n}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-perl-js.da6eda82.js b/docs/js/highlight-js-perl-js.da6eda82.js new file mode 100644 index 0000000..5a05386 --- /dev/null +++ b/docs/js/highlight-js-perl-js.da6eda82.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[645],{2482:function(e){function n(e){const n=e.regex,t=["abs","accept","alarm","and","atan2","bind","binmode","bless","break","caller","chdir","chmod","chomp","chop","chown","chr","chroot","close","closedir","connect","continue","cos","crypt","dbmclose","dbmopen","defined","delete","die","do","dump","each","else","elsif","endgrent","endhostent","endnetent","endprotoent","endpwent","endservent","eof","eval","exec","exists","exit","exp","fcntl","fileno","flock","for","foreach","fork","format","formline","getc","getgrent","getgrgid","getgrnam","gethostbyaddr","gethostbyname","gethostent","getlogin","getnetbyaddr","getnetbyname","getnetent","getpeername","getpgrp","getpriority","getprotobyname","getprotobynumber","getprotoent","getpwent","getpwnam","getpwuid","getservbyname","getservbyport","getservent","getsockname","getsockopt","given","glob","gmtime","goto","grep","gt","hex","if","index","int","ioctl","join","keys","kill","last","lc","lcfirst","length","link","listen","local","localtime","log","lstat","lt","ma","map","mkdir","msgctl","msgget","msgrcv","msgsnd","my","ne","next","no","not","oct","open","opendir","or","ord","our","pack","package","pipe","pop","pos","print","printf","prototype","push","q|0","qq","quotemeta","qw","qx","rand","read","readdir","readline","readlink","readpipe","recv","redo","ref","rename","require","reset","return","reverse","rewinddir","rindex","rmdir","say","scalar","seek","seekdir","select","semctl","semget","semop","send","setgrent","sethostent","setnetent","setpgrp","setpriority","setprotoent","setpwent","setservent","setsockopt","shift","shmctl","shmget","shmread","shmwrite","shutdown","sin","sleep","socket","socketpair","sort","splice","split","sprintf","sqrt","srand","stat","state","study","sub","substr","symlink","syscall","sysopen","sysread","sysseek","system","syswrite","tell","telldir","tie","tied","time","times","tr","truncate","uc","ucfirst","umask","undef","unless","unlink","unpack","unshift","untie","until","use","utime","values","vec","wait","waitpid","wantarray","warn","when","while","write","x|0","xor","y|0"],r=/[dualxmsipngr]{0,12}/,s={$pattern:/[\w.]+/,keyword:t.join(" ")},i={className:"subst",begin:"[$@]\\{",end:"\\}",keywords:s},a={begin:/->\{/,end:/\}/},c={variants:[{begin:/\$\d/},{begin:n.concat(/[$%@](\^\w\b|#\w+(::\w+)*|\{\w+\}|\w+(::\w*)*)/,"(?![A-Za-z])(?![@$%])")},{begin:/[$%@][^\s\w{]/,relevance:0}]},o=[e.BACKSLASH_ESCAPE,i,c],g=[/!/,/\//,/\|/,/\?/,/'/,/"/,/#/],l=(e,t,s="\\1")=>{const i="\\1"===s?s:n.concat(s,t);return n.concat(n.concat("(?:",e,")"),t,/(?:\\.|[^\\\/])*?/,i,/(?:\\.|[^\\\/])*?/,s,r)},d=(e,t,s)=>n.concat(n.concat("(?:",e,")"),t,/(?:\\.|[^\\\/])*?/,s,r),p=[c,e.HASH_COMMENT_MODE,e.COMMENT(/^=\w/,/=cut/,{endsWithParent:!0}),a,{className:"string",contains:o,variants:[{begin:"q[qwxr]?\\s*\\(",end:"\\)",relevance:5},{begin:"q[qwxr]?\\s*\\[",end:"\\]",relevance:5},{begin:"q[qwxr]?\\s*\\{",end:"\\}",relevance:5},{begin:"q[qwxr]?\\s*\\|",end:"\\|",relevance:5},{begin:"q[qwxr]?\\s*<",end:">",relevance:5},{begin:"qw\\s+q",end:"q",relevance:5},{begin:"'",end:"'",contains:[e.BACKSLASH_ESCAPE]},{begin:'"',end:'"'},{begin:"`",end:"`",contains:[e.BACKSLASH_ESCAPE]},{begin:/\{\w+\}/,relevance:0},{begin:"-?\\w+\\s*=>",relevance:0}]},{className:"number",begin:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",relevance:0},{begin:"(\\/\\/|"+e.RE_STARTERS_RE+"|\\b(split|return|print|reverse|grep)\\b)\\s*",keywords:"split return print reverse grep",relevance:0,contains:[e.HASH_COMMENT_MODE,{className:"regexp",variants:[{begin:l("s|tr|y",n.either(...g,{capture:!0}))},{begin:l("s|tr|y","\\(","\\)")},{begin:l("s|tr|y","\\[","\\]")},{begin:l("s|tr|y","\\{","\\}")}],relevance:2},{className:"regexp",variants:[{begin:/(m|qr)\/\//,relevance:0},{begin:d("(?:m|qr)?",/\//,/\//)},{begin:d("m|qr",n.either(...g,{capture:!0}),/\1/)},{begin:d("m|qr",/\(/,/\)/)},{begin:d("m|qr",/\[/,/\]/)},{begin:d("m|qr",/\{/,/\}/)}]}]},{className:"function",beginKeywords:"sub",end:"(\\s*\\(.*?\\))?[;{]",excludeEnd:!0,relevance:5,contains:[e.TITLE_MODE]},{begin:"-\\w\\b",relevance:0},{begin:"^__DATA__$",end:"^__END__$",subLanguage:"mojolicious",contains:[{begin:"^@@.*",end:"$",className:"comment"}]}];return i.contains=p,a.contains=p,{name:"Perl",aliases:["pl","pm"],keywords:s,contains:p}}e.exports=n}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-php-js.c458ffa4.js b/docs/js/highlight-js-php-js.c458ffa4.js new file mode 100644 index 0000000..344966a --- /dev/null +++ b/docs/js/highlight-js-php-js.c458ffa4.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[596],{2656:function(e){function r(e){const r={className:"variable",begin:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*(?![A-Za-z0-9])(?![$])"},t={className:"meta",variants:[{begin:/<\?php/,relevance:10},{begin:/<\?[=]?/},{begin:/\?>/}]},a={className:"subst",variants:[{begin:/\$\w+/},{begin:/\{\$/,end:/\}/}]},n=e.inherit(e.APOS_STRING_MODE,{illegal:null}),i=e.inherit(e.QUOTE_STRING_MODE,{illegal:null,contains:e.QUOTE_STRING_MODE.contains.concat(a)}),o=e.END_SAME_AS_BEGIN({begin:/<<<[ \t]*(\w+)\n/,end:/[ \t]*(\w+)\b/,contains:e.QUOTE_STRING_MODE.contains.concat(a)}),l={className:"string",contains:[e.BACKSLASH_ESCAPE,t],variants:[e.inherit(n,{begin:"b'",end:"'"}),e.inherit(i,{begin:'b"',end:'"'}),i,n,o]},c={className:"number",variants:[{begin:"\\b0b[01]+(?:_[01]+)*\\b"},{begin:"\\b0o[0-7]+(?:_[0-7]+)*\\b"},{begin:"\\b0x[\\da-f]+(?:_[\\da-f]+)*\\b"},{begin:"(?:\\b\\d+(?:_\\d+)*(\\.(?:\\d+(?:_\\d+)*))?|\\B\\.\\d+)(?:e[+-]?\\d+)?"}],relevance:0},s={keyword:"__CLASS__ __DIR__ __FILE__ __FUNCTION__ __LINE__ __METHOD__ __NAMESPACE__ __TRAIT__ die echo exit include include_once print require require_once array abstract and as binary bool boolean break callable case catch class clone const continue declare default do double else elseif empty enddeclare endfor endforeach endif endswitch endwhile enum eval extends final finally float for foreach from global goto if implements instanceof insteadof int integer interface isset iterable list match|0 mixed new object or private protected public real return string switch throw trait try unset use var void while xor yield",literal:"false null true",built_in:"Error|0 AppendIterator ArgumentCountError ArithmeticError ArrayIterator ArrayObject AssertionError BadFunctionCallException BadMethodCallException CachingIterator CallbackFilterIterator CompileError Countable DirectoryIterator DivisionByZeroError DomainException EmptyIterator ErrorException Exception FilesystemIterator FilterIterator GlobIterator InfiniteIterator InvalidArgumentException IteratorIterator LengthException LimitIterator LogicException MultipleIterator NoRewindIterator OutOfBoundsException OutOfRangeException OuterIterator OverflowException ParentIterator ParseError RangeException RecursiveArrayIterator RecursiveCachingIterator RecursiveCallbackFilterIterator RecursiveDirectoryIterator RecursiveFilterIterator RecursiveIterator RecursiveIteratorIterator RecursiveRegexIterator RecursiveTreeIterator RegexIterator RuntimeException SeekableIterator SplDoublyLinkedList SplFileInfo SplFileObject SplFixedArray SplHeap SplMaxHeap SplMinHeap SplObjectStorage SplObserver SplObserver SplPriorityQueue SplQueue SplStack SplSubject SplSubject SplTempFileObject TypeError UnderflowException UnexpectedValueException UnhandledMatchError ArrayAccess Closure Generator Iterator IteratorAggregate Serializable Stringable Throwable Traversable WeakReference WeakMap Directory __PHP_Incomplete_Class parent php_user_filter self static stdClass"};return{case_insensitive:!0,keywords:s,contains:[e.HASH_COMMENT_MODE,e.COMMENT("//","$",{contains:[t]}),e.COMMENT("/\\*","\\*/",{contains:[{className:"doctag",begin:"@[A-Za-z]+"}]}),e.COMMENT("__halt_compiler.+?;",!1,{endsWithParent:!0,keywords:"__halt_compiler"}),t,{className:"keyword",begin:/\$this\b/},r,{begin:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{className:"function",relevance:0,beginKeywords:"fn function",end:/[;{]/,excludeEnd:!0,illegal:"[$%\\[]",contains:[{beginKeywords:"use"},e.UNDERSCORE_TITLE_MODE,{begin:"=>",endsParent:!0},{className:"params",begin:"\\(",end:"\\)",excludeBegin:!0,excludeEnd:!0,keywords:s,contains:["self",r,e.C_BLOCK_COMMENT_MODE,l,c]}]},{className:"class",variants:[{beginKeywords:"enum",illegal:/[($"]/},{beginKeywords:"class interface trait",illegal:/[:($"]/}],relevance:0,end:/\{/,excludeEnd:!0,contains:[{beginKeywords:"extends implements"},e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"namespace",relevance:0,end:";",illegal:/[.']/,contains:[e.UNDERSCORE_TITLE_MODE]},{beginKeywords:"use",relevance:0,end:";",contains:[e.UNDERSCORE_TITLE_MODE]},l,c]}}e.exports=r}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-python-js.60354774.js b/docs/js/highlight-js-python-js.60354774.js new file mode 100644 index 0000000..651bc25 --- /dev/null +++ b/docs/js/highlight-js-python-js.60354774.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[435],{8245:function(e){function n(e){const n=e.regex,a=/[\p{XID_Start}_]\p{XID_Continue}*/u,i=["and","as","assert","async","await","break","class","continue","def","del","elif","else","except","finally","for","from","global","if","import","in","is","lambda","nonlocal|10","not","or","pass","raise","return","try","while","with","yield"],s=["__import__","abs","all","any","ascii","bin","bool","breakpoint","bytearray","bytes","callable","chr","classmethod","compile","complex","delattr","dict","dir","divmod","enumerate","eval","exec","filter","float","format","frozenset","getattr","globals","hasattr","hash","help","hex","id","input","int","isinstance","issubclass","iter","len","list","locals","map","max","memoryview","min","next","object","oct","open","ord","pow","print","property","range","repr","reversed","round","set","setattr","slice","sorted","staticmethod","str","sum","super","tuple","type","vars","zip"],t=["__debug__","Ellipsis","False","None","NotImplemented","True"],r=["Any","Callable","Coroutine","Dict","List","Literal","Generic","Optional","Sequence","Set","Tuple","Type","Union"],l={$pattern:/[A-Za-z]\w+|__\w+__/,keyword:i,built_in:s,literal:t,type:r},b={className:"meta",begin:/^(>>>|\.\.\.) /},o={className:"subst",begin:/\{/,end:/\}/,keywords:l,illegal:/#/},c={begin:/\{\{/,relevance:0},d={className:"string",contains:[e.BACKSLASH_ESCAPE],variants:[{begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,b],relevance:10},{begin:/([uU]|[bB]|[rR]|[bB][rR]|[rR][bB])?"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,b],relevance:10},{begin:/([fF][rR]|[rR][fF]|[fF])'''/,end:/'''/,contains:[e.BACKSLASH_ESCAPE,b,c,o]},{begin:/([fF][rR]|[rR][fF]|[fF])"""/,end:/"""/,contains:[e.BACKSLASH_ESCAPE,b,c,o]},{begin:/([uU]|[rR])'/,end:/'/,relevance:10},{begin:/([uU]|[rR])"/,end:/"/,relevance:10},{begin:/([bB]|[bB][rR]|[rR][bB])'/,end:/'/},{begin:/([bB]|[bB][rR]|[rR][bB])"/,end:/"/},{begin:/([fF][rR]|[rR][fF]|[fF])'/,end:/'/,contains:[e.BACKSLASH_ESCAPE,c,o]},{begin:/([fF][rR]|[rR][fF]|[fF])"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,c,o]},e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},p="[0-9](_?[0-9])*",_=`(\\b(${p}))?\\.(${p})|\\b(${p})\\.`,g={className:"number",relevance:0,variants:[{begin:`(\\b(${p})|(${_}))[eE][+-]?(${p})[jJ]?\\b`},{begin:`(${_})[jJ]?`},{begin:"\\b([1-9](_?[0-9])*|0+(_?0)*)[lLjJ]?\\b"},{begin:"\\b0[bB](_?[01])+[lL]?\\b"},{begin:"\\b0[oO](_?[0-7])+[lL]?\\b"},{begin:"\\b0[xX](_?[0-9a-fA-F])+[lL]?\\b"},{begin:`\\b(${p})[jJ]\\b`}]},m={className:"comment",begin:n.lookahead(/# type:/),end:/$/,keywords:l,contains:[{begin:/# type:/},{begin:/#/,end:/\b\B/,endsWithParent:!0}]},f={className:"params",variants:[{className:"",begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:l,contains:["self",b,g,d,e.HASH_COMMENT_MODE]}]};return o.contains=[d,g,b],{name:"Python",aliases:["py","gyp","ipython"],unicodeRegex:!0,keywords:l,illegal:/(<\/|->|\?)|=>/,contains:[b,g,{begin:/\bself\b/},{beginKeywords:"if",relevance:0},d,m,e.HASH_COMMENT_MODE,{match:[/def/,/\s+/,a],scope:{1:"keyword",3:"title.function"},contains:[f]},{variants:[{match:[/class/,/\s+/,a,/\s*/,/\(\s*/,a,/\s*\)/]},{match:[/class/,/\s+/,a]}],scope:{1:"keyword",3:"title.class",6:"title.class.inherited"}},{className:"meta",begin:/^[\t ]*@/,end:/(?=#)|$/,contains:[g,f,d]}]}}e.exports=n}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-ruby-js.7272231f.js b/docs/js/highlight-js-ruby-js.7272231f.js new file mode 100644 index 0000000..d63f527 --- /dev/null +++ b/docs/js/highlight-js-ruby-js.7272231f.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[623],{7905:function(e){function n(e){const n=e.regex,a="([a-zA-Z_]\\w*[!?=]?|[-+~]@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?)",i={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor __FILE__",built_in:"proc lambda",literal:"true false nil"},s={className:"doctag",begin:"@[A-Za-z]+"},c={begin:"#<",end:">"},b=[e.COMMENT("#","$",{contains:[s]}),e.COMMENT("^=begin","^=end",{contains:[s],relevance:10}),e.COMMENT("^__END__","\\n$")],r={className:"subst",begin:/#\{/,end:/\}/,keywords:i},d={className:"string",contains:[e.BACKSLASH_ESCAPE,r],variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/`/,end:/`/},{begin:/%[qQwWx]?\(/,end:/\)/},{begin:/%[qQwWx]?\[/,end:/\]/},{begin:/%[qQwWx]?\{/,end:/\}/},{begin:/%[qQwWx]?/},{begin:/%[qQwWx]?\//,end:/\//},{begin:/%[qQwWx]?%/,end:/%/},{begin:/%[qQwWx]?-/,end:/-/},{begin:/%[qQwWx]?\|/,end:/\|/},{begin:/\B\?(\\\d{1,3})/},{begin:/\B\?(\\x[A-Fa-f0-9]{1,2})/},{begin:/\B\?(\\u\{?[A-Fa-f0-9]{1,6}\}?)/},{begin:/\B\?(\\M-\\C-|\\M-\\c|\\c\\M-|\\M-|\\C-\\M-)[\x20-\x7e]/},{begin:/\B\?\\(c|C-)[\x20-\x7e]/},{begin:/\B\?\\?\S/},{begin:n.concat(/<<[-~]?'?/,n.lookahead(/(\w+)(?=\W)[^\n]*\n(?:[^\n]*\n)*?\s*\1\b/)),contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,contains:[e.BACKSLASH_ESCAPE,r]})]}]},t="[1-9](_?[0-9])*|0",l="[0-9](_?[0-9])*",o={className:"number",relevance:0,variants:[{begin:`\\b(${t})(\\.(${l}))?([eE][+-]?(${l})|r)?i?\\b`},{begin:"\\b0[dD][0-9](_?[0-9])*r?i?\\b"},{begin:"\\b0[bB][0-1](_?[0-1])*r?i?\\b"},{begin:"\\b0[oO][0-7](_?[0-7])*r?i?\\b"},{begin:"\\b0[xX][0-9a-fA-F](_?[0-9a-fA-F])*r?i?\\b"},{begin:"\\b0(_?[0-7])+r?i?\\b"}]},g={className:"params",begin:"\\(",end:"\\)",endsParent:!0,keywords:i},_=[d,{className:"class",beginKeywords:"class module",end:"$|;",illegal:/=/,contains:[e.inherit(e.TITLE_MODE,{begin:"[A-Za-z_]\\w*(::\\w+)*(\\?|!)?"}),{begin:"<\\s*",contains:[{begin:"("+e.IDENT_RE+"::)?"+e.IDENT_RE,relevance:0}]}].concat(b)},{className:"function",begin:n.concat(/def\s+/,n.lookahead(a+"\\s*(\\(|;|$)")),relevance:0,keywords:"def",end:"$|;",contains:[e.inherit(e.TITLE_MODE,{begin:a}),g].concat(b)},{begin:e.IDENT_RE+"::"},{className:"symbol",begin:e.UNDERSCORE_IDENT_RE+"(!|\\?)?:",relevance:0},{className:"symbol",begin:":(?!\\s)",contains:[d,{begin:a}],relevance:0},o,{className:"variable",begin:"(\\$\\W)|((\\$|@@?)(\\w+))(?=[^@$?])(?![A-Za-z])(?![@$?'])"},{className:"params",begin:/\|/,end:/\|/,relevance:0,keywords:i},{begin:"("+e.RE_STARTERS_RE+"|unless)\\s*",keywords:"unless",contains:[{className:"regexp",contains:[e.BACKSLASH_ESCAPE,r],illegal:/\n/,variants:[{begin:"/",end:"/[a-z]*"},{begin:/%r\{/,end:/\}[a-z]*/},{begin:"%r\\(",end:"\\)[a-z]*"},{begin:"%r!",end:"![a-z]*"},{begin:"%r\\[",end:"\\][a-z]*"}]}].concat(c,b),relevance:0}].concat(c,b);r.contains=_,g.contains=_;const E="[>?]>",w="[\\w#]+\\(\\w+\\):\\d+:\\d+>",u="(\\w+-)?\\d+\\.\\d+\\.\\d+(p\\d+)?[^\\d][^>]+>",N=[{begin:/^\s*=>/,starts:{end:"$",contains:_}},{className:"meta",begin:"^("+E+"|"+w+"|"+u+")(?=[ ])",starts:{end:"$",contains:_}}];return b.unshift(c),{name:"Ruby",aliases:["rb","gemspec","podspec","thor","irb"],keywords:i,illegal:/\/\*/,contains:[e.SHEBANG({binary:"ruby"})].concat(N).concat(b).concat(_)}}e.exports=n}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-scss-js.adcd11a2.js b/docs/js/highlight-js-scss-js.adcd11a2.js new file mode 100644 index 0000000..a8b5b9f --- /dev/null +++ b/docs/js/highlight-js-scss-js.adcd11a2.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[392],{1062:function(e){const t=e=>({IMPORTANT:{scope:"meta",begin:"!important"},BLOCK_COMMENT:e.C_BLOCK_COMMENT_MODE,HEXCOLOR:{scope:"number",begin:/#(([0-9a-fA-F]{3,4})|(([0-9a-fA-F]{2}){3,4}))\b/},FUNCTION_DISPATCH:{className:"built_in",begin:/[\w-]+(?=\()/},ATTRIBUTE_SELECTOR_MODE:{scope:"selector-attr",begin:/\[/,end:/\]/,illegal:"$",contains:[e.APOS_STRING_MODE,e.QUOTE_STRING_MODE]},CSS_NUMBER_MODE:{scope:"number",begin:e.NUMBER_RE+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},CSS_VARIABLE:{className:"attr",begin:/--[A-Za-z][A-Za-z0-9_-]*/}}),i=["a","abbr","address","article","aside","audio","b","blockquote","body","button","canvas","caption","cite","code","dd","del","details","dfn","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","header","hgroup","html","i","iframe","img","input","ins","kbd","label","legend","li","main","mark","menu","nav","object","ol","p","q","quote","samp","section","span","strong","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","tr","ul","var","video"],r=["any-hover","any-pointer","aspect-ratio","color","color-gamut","color-index","device-aspect-ratio","device-height","device-width","display-mode","forced-colors","grid","height","hover","inverted-colors","monochrome","orientation","overflow-block","overflow-inline","pointer","prefers-color-scheme","prefers-contrast","prefers-reduced-motion","prefers-reduced-transparency","resolution","scan","scripting","update","width","min-width","max-width","min-height","max-height"],o=["active","any-link","blank","checked","current","default","defined","dir","disabled","drop","empty","enabled","first","first-child","first-of-type","fullscreen","future","focus","focus-visible","focus-within","has","host","host-context","hover","indeterminate","in-range","invalid","is","lang","last-child","last-of-type","left","link","local-link","not","nth-child","nth-col","nth-last-child","nth-last-col","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","past","placeholder-shown","read-only","read-write","required","right","root","scope","target","target-within","user-invalid","valid","visited","where"],a=["after","backdrop","before","cue","cue-region","first-letter","first-line","grammar-error","marker","part","placeholder","selection","slotted","spelling-error"],n=["align-content","align-items","align-self","all","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","caret-color","clear","clip","clip-path","clip-rule","color","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","contain","content","content-visibility","counter-increment","counter-reset","cue","cue-after","cue-before","cursor","direction","display","empty-cells","filter","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","flow","font","font-display","font-family","font-feature-settings","font-kerning","font-language-override","font-size","font-size-adjust","font-smoothing","font-stretch","font-style","font-synthesis","font-variant","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-variation-settings","font-weight","gap","glyph-orientation-vertical","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-gap","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","ime-mode","isolation","justify-content","left","letter-spacing","line-break","line-height","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marks","mask","mask-border","mask-border-mode","mask-border-outset","mask-border-repeat","mask-border-slice","mask-border-source","mask-border-width","mask-clip","mask-composite","mask-image","mask-mode","mask-origin","mask-position","mask-repeat","mask-size","mask-type","max-height","max-width","min-height","min-width","mix-blend-mode","nav-down","nav-index","nav-left","nav-right","nav-up","none","normal","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page-break-after","page-break-before","page-break-inside","pause","pause-after","pause-before","perspective","perspective-origin","pointer-events","position","quotes","resize","rest","rest-after","rest-before","right","row-gap","scroll-margin","scroll-margin-block","scroll-margin-block-end","scroll-margin-block-start","scroll-margin-bottom","scroll-margin-inline","scroll-margin-inline-end","scroll-margin-inline-start","scroll-margin-left","scroll-margin-right","scroll-margin-top","scroll-padding","scroll-padding-block","scroll-padding-block-end","scroll-padding-block-start","scroll-padding-bottom","scroll-padding-inline","scroll-padding-inline-end","scroll-padding-inline-start","scroll-padding-left","scroll-padding-right","scroll-padding-top","scroll-snap-align","scroll-snap-stop","scroll-snap-type","shape-image-threshold","shape-margin","shape-outside","speak","speak-as","src","tab-size","table-layout","text-align","text-align-all","text-align-last","text-combine-upright","text-decoration","text-decoration-color","text-decoration-line","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-indent","text-justify","text-orientation","text-overflow","text-rendering","text-shadow","text-transform","text-underline-position","top","transform","transform-box","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","writing-mode","z-index"].reverse();function l(e){const l=t(e),s=a,d=o,c="@[a-z-]+",p="and or not only",g="[a-zA-Z-][a-zA-Z0-9_-]*",m={className:"variable",begin:"(\\$"+g+")\\b"};return{name:"SCSS",case_insensitive:!0,illegal:"[=/|']",contains:[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE,l.CSS_NUMBER_MODE,{className:"selector-id",begin:"#[A-Za-z0-9_-]+",relevance:0},{className:"selector-class",begin:"\\.[A-Za-z0-9_-]+",relevance:0},l.ATTRIBUTE_SELECTOR_MODE,{className:"selector-tag",begin:"\\b("+i.join("|")+")\\b",relevance:0},{className:"selector-pseudo",begin:":("+d.join("|")+")"},{className:"selector-pseudo",begin:":(:)?("+s.join("|")+")"},m,{begin:/\(/,end:/\)/,contains:[l.CSS_NUMBER_MODE]},l.CSS_VARIABLE,{className:"attribute",begin:"\\b("+n.join("|")+")\\b"},{begin:"\\b(whitespace|wait|w-resize|visible|vertical-text|vertical-ideographic|uppercase|upper-roman|upper-alpha|underline|transparent|top|thin|thick|text|text-top|text-bottom|tb-rl|table-header-group|table-footer-group|sw-resize|super|strict|static|square|solid|small-caps|separate|se-resize|scroll|s-resize|rtl|row-resize|ridge|right|repeat|repeat-y|repeat-x|relative|progress|pointer|overline|outside|outset|oblique|nowrap|not-allowed|normal|none|nw-resize|no-repeat|no-drop|newspaper|ne-resize|n-resize|move|middle|medium|ltr|lr-tb|lowercase|lower-roman|lower-alpha|loose|list-item|line|line-through|line-edge|lighter|left|keep-all|justify|italic|inter-word|inter-ideograph|inside|inset|inline|inline-block|inherit|inactive|ideograph-space|ideograph-parenthesis|ideograph-numeric|ideograph-alpha|horizontal|hidden|help|hand|groove|fixed|ellipsis|e-resize|double|dotted|distribute|distribute-space|distribute-letter|distribute-all-lines|disc|disabled|default|decimal|dashed|crosshair|collapse|col-resize|circle|char|center|capitalize|break-word|break-all|bottom|both|bolder|bold|block|bidi-override|below|baseline|auto|always|all-scroll|absolute|table|table-cell)\\b"},{begin:/:/,end:/[;}{]/,contains:[l.BLOCK_COMMENT,m,l.HEXCOLOR,l.CSS_NUMBER_MODE,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,l.IMPORTANT]},{begin:"@(page|font-face)",keywords:{$pattern:c,keyword:"@page @font-face"}},{begin:"@",end:"[{;]",returnBegin:!0,keywords:{$pattern:/[a-z-]+/,keyword:p,attribute:r.join(" ")},contains:[{begin:c,className:"keyword"},{begin:/[a-z-]+(?=:)/,className:"attribute"},m,e.QUOTE_STRING_MODE,e.APOS_STRING_MODE,l.HEXCOLOR,l.CSS_NUMBER_MODE]},l.FUNCTION_DISPATCH]}}e.exports=l}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-shell-js.0ad5b20f.js b/docs/js/highlight-js-shell-js.0ad5b20f.js new file mode 100644 index 0000000..e9e90cf --- /dev/null +++ b/docs/js/highlight-js-shell-js.0ad5b20f.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[176],{7874:function(s){function e(s){return{name:"Shell Session",aliases:["console","shellsession"],contains:[{className:"meta",begin:/^\s{0,3}[/~\w\d[\]()@-]*[>%$#][ ]?/,starts:{end:/[^\\](?=\s*$)/,subLanguage:"bash"}}]}}s.exports=e}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-swift-js.bdd5bff5.js b/docs/js/highlight-js-swift-js.bdd5bff5.js new file mode 100644 index 0000000..2107fba --- /dev/null +++ b/docs/js/highlight-js-swift-js.bdd5bff5.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[527],{7690:function(e){function n(e){return e?"string"===typeof e?e:e.source:null}function t(e){return a("(?=",e,")")}function a(...e){const t=e.map((e=>n(e))).join("");return t}function i(e){const n=e[e.length-1];return"object"===typeof n&&n.constructor===Object?(e.splice(e.length-1,1),n):{}}function s(...e){const t=i(e),a="("+(t.capture?"":"?:")+e.map((e=>n(e))).join("|")+")";return a}const c=e=>a(/\b/,e,/\w$/.test(e)?/\b/:/\B/),u=["Protocol","Type"].map(c),o=["init","self"].map(c),r=["Any","Self"],l=["actor","associatedtype","async","await",/as\?/,/as!/,"as","break","case","catch","class","continue","convenience","default","defer","deinit","didSet","do","dynamic","else","enum","extension","fallthrough",/fileprivate\(set\)/,"fileprivate","final","for","func","get","guard","if","import","indirect","infix",/init\?/,/init!/,"inout",/internal\(set\)/,"internal","in","is","isolated","nonisolated","lazy","let","mutating","nonmutating",/open\(set\)/,"open","operator","optional","override","postfix","precedencegroup","prefix",/private\(set\)/,"private","protocol",/public\(set\)/,"public","repeat","required","rethrows","return","set","some","static","struct","subscript","super","switch","throws","throw",/try\?/,/try!/,"try","typealias",/unowned\(safe\)/,/unowned\(unsafe\)/,"unowned","var","weak","where","while","willSet"],m=["false","nil","true"],p=["assignment","associativity","higherThan","left","lowerThan","none","right"],d=["#colorLiteral","#column","#dsohandle","#else","#elseif","#endif","#error","#file","#fileID","#fileLiteral","#filePath","#function","#if","#imageLiteral","#keyPath","#line","#selector","#sourceLocation","#warn_unqualified_access","#warning"],F=["abs","all","any","assert","assertionFailure","debugPrint","dump","fatalError","getVaList","isKnownUniquelyReferenced","max","min","numericCast","pointwiseMax","pointwiseMin","precondition","preconditionFailure","print","readLine","repeatElement","sequence","stride","swap","swift_unboxFromSwiftValueWithType","transcode","type","unsafeBitCast","unsafeDowncast","withExtendedLifetime","withUnsafeMutablePointer","withUnsafePointer","withVaList","withoutActuallyEscaping","zip"],b=s(/[/=\-+!*%<>&|^~?]/,/[\u00A1-\u00A7]/,/[\u00A9\u00AB]/,/[\u00AC\u00AE]/,/[\u00B0\u00B1]/,/[\u00B6\u00BB\u00BF\u00D7\u00F7]/,/[\u2016-\u2017]/,/[\u2020-\u2027]/,/[\u2030-\u203E]/,/[\u2041-\u2053]/,/[\u2055-\u205E]/,/[\u2190-\u23FF]/,/[\u2500-\u2775]/,/[\u2794-\u2BFF]/,/[\u2E00-\u2E7F]/,/[\u3001-\u3003]/,/[\u3008-\u3020]/,/[\u3030]/),f=s(b,/[\u0300-\u036F]/,/[\u1DC0-\u1DFF]/,/[\u20D0-\u20FF]/,/[\uFE00-\uFE0F]/,/[\uFE20-\uFE2F]/),h=a(b,f,"*"),w=s(/[a-zA-Z_]/,/[\u00A8\u00AA\u00AD\u00AF\u00B2-\u00B5\u00B7-\u00BA]/,/[\u00BC-\u00BE\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF]/,/[\u0100-\u02FF\u0370-\u167F\u1681-\u180D\u180F-\u1DBF]/,/[\u1E00-\u1FFF]/,/[\u200B-\u200D\u202A-\u202E\u203F-\u2040\u2054\u2060-\u206F]/,/[\u2070-\u20CF\u2100-\u218F\u2460-\u24FF\u2776-\u2793]/,/[\u2C00-\u2DFF\u2E80-\u2FFF]/,/[\u3004-\u3007\u3021-\u302F\u3031-\u303F\u3040-\uD7FF]/,/[\uF900-\uFD3D\uFD40-\uFDCF\uFDF0-\uFE1F\uFE30-\uFE44]/,/[\uFE47-\uFEFE\uFF00-\uFFFD]/),y=s(w,/\d/,/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE20-\uFE2F]/),g=a(w,y,"*"),E=a(/[A-Z]/,y,"*"),v=["autoclosure",a(/convention\(/,s("swift","block","c"),/\)/),"discardableResult","dynamicCallable","dynamicMemberLookup","escaping","frozen","GKInspectable","IBAction","IBDesignable","IBInspectable","IBOutlet","IBSegueAction","inlinable","main","nonobjc","NSApplicationMain","NSCopying","NSManaged",a(/objc\(/,g,/\)/),"objc","objcMembers","propertyWrapper","requires_stored_property_inits","resultBuilder","testable","UIApplicationMain","unknown","usableFromInline"],A=["iOS","iOSApplicationExtension","macOS","macOSApplicationExtension","macCatalyst","macCatalystApplicationExtension","watchOS","watchOSApplicationExtension","tvOS","tvOSApplicationExtension","swift"];function N(e){const n={match:/\s+/,relevance:0},i=e.COMMENT("/\\*","\\*/",{contains:["self"]}),b=[e.C_LINE_COMMENT_MODE,i],w={match:[/\./,s(...u,...o)],className:{2:"keyword"}},N={match:a(/\./,s(...l)),relevance:0},C=l.filter((e=>"string"===typeof e)).concat(["_|0"]),k=l.filter((e=>"string"!==typeof e)).concat(r).map(c),D={variants:[{className:"keyword",match:s(...k,...o)}]},B={$pattern:s(/\b\w+/,/#\w+/),keyword:C.concat(d),literal:m},_=[w,N,D],S={match:a(/\./,s(...F)),relevance:0},M={className:"built_in",match:a(/\b/,s(...F),/(?=\()/)},x=[S,M],$={match:/->/,relevance:0},I={className:"operator",relevance:0,variants:[{match:h},{match:`\\.(\\.|${f})+`}]},O=[$,I],L="([0-9]_*)+",T="([0-9a-fA-F]_*)+",P={className:"number",relevance:0,variants:[{match:`\\b(${L})(\\.(${L}))?([eE][+-]?(${L}))?\\b`},{match:`\\b0x(${T})(\\.(${T}))?([pP][+-]?(${L}))?\\b`},{match:/\b0o([0-7]_*)+\b/},{match:/\b0b([01]_*)+\b/}]},j=(e="")=>({className:"subst",variants:[{match:a(/\\/,e,/[0\\tnr"']/)},{match:a(/\\/,e,/u\{[0-9a-fA-F]{1,8}\}/)}]}),K=(e="")=>({className:"subst",match:a(/\\/,e,/[\t ]*(?:[\r\n]|\r\n)/)}),z=(e="")=>({className:"subst",label:"interpol",begin:a(/\\/,e,/\(/),end:/\)/}),q=(e="")=>({begin:a(e,/"""/),end:a(/"""/,e),contains:[j(e),K(e),z(e)]}),U=(e="")=>({begin:a(e,/"/),end:a(/"/,e),contains:[j(e),z(e)]}),Z={className:"string",variants:[q(),q("#"),q("##"),q("###"),U(),U("#"),U("##"),U("###")]},V={match:a(/`/,g,/`/)},W={className:"variable",match:/\$\d+/},G={className:"variable",match:`\\$${y}+`},R=[V,W,G],X={match:/(@|#)available/,className:"keyword",starts:{contains:[{begin:/\(/,end:/\)/,keywords:A,contains:[...O,P,Z]}]}},H={className:"keyword",match:a(/@/,s(...v))},J={className:"meta",match:a(/@/,g)},Q=[X,H,J],Y={match:t(/\b[A-Z]/),relevance:0,contains:[{className:"type",match:a(/(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)/,y,"+")},{className:"type",match:E,relevance:0},{match:/[?!]+/,relevance:0},{match:/\.\.\./,relevance:0},{match:a(/\s+&\s+/,t(E)),relevance:0}]},ee={begin://,keywords:B,contains:[...b,..._,...Q,$,Y]};Y.contains.push(ee);const ne={match:a(g,/\s*:/),keywords:"_|0",relevance:0},te={begin:/\(/,end:/\)/,relevance:0,keywords:B,contains:["self",ne,...b,..._,...x,...O,P,Z,...R,...Q,Y]},ae={begin://,contains:[...b,Y]},ie={begin:s(t(a(g,/\s*:/)),t(a(g,/\s+/,g,/\s*:/))),end:/:/,relevance:0,contains:[{className:"keyword",match:/\b_\b/},{className:"params",match:g}]},se={begin:/\(/,end:/\)/,keywords:B,contains:[ie,...b,..._,...O,P,Z,...Q,Y,te],endsParent:!0,illegal:/["']/},ce={match:[/func/,/\s+/,s(V.match,g,h)],className:{1:"keyword",3:"title.function"},contains:[ae,se,n],illegal:[/\[/,/%/]},ue={match:[/\b(?:subscript|init[?!]?)/,/\s*(?=[<(])/],className:{1:"keyword"},contains:[ae,se,n],illegal:/\[|%/},oe={match:[/operator/,/\s+/,h],className:{1:"keyword",3:"title"}},re={begin:[/precedencegroup/,/\s+/,E],className:{1:"keyword",3:"title"},contains:[Y],keywords:[...p,...m],end:/}/};for(const t of Z.variants){const e=t.contains.find((e=>"interpol"===e.label));e.keywords=B;const n=[..._,...x,...O,P,Z,...R];e.contains=[...n,{begin:/\(/,end:/\)/,contains:["self",...n]}]}return{name:"Swift",keywords:B,contains:[...b,ce,ue,{beginKeywords:"struct protocol class extension enum actor",end:"\\{",excludeEnd:!0,keywords:B,contains:[e.inherit(e.TITLE_MODE,{className:"title.class",begin:/[A-Za-z$_][\u00C0-\u02B80-9A-Za-z$_]*/}),..._]},oe,re,{beginKeywords:"import",end:/$/,contains:[...b],relevance:0},..._,...x,...O,P,Z,...R,...Q,Y,te]}}e.exports=N}}]); \ No newline at end of file diff --git a/docs/js/highlight-js-xml-js.0d78f903.js b/docs/js/highlight-js-xml-js.0d78f903.js new file mode 100644 index 0000000..7eb3ddd --- /dev/null +++ b/docs/js/highlight-js-xml-js.0d78f903.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[490],{4610:function(e){function n(e){const n=e.regex,a=n.concat(/[A-Z_]/,n.optional(/[A-Z0-9_.-]*:/),/[A-Z0-9_.-]*/),s=/[A-Za-z0-9._:-]+/,t={className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},c={begin:/\s/,contains:[{className:"keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},i=e.inherit(c,{begin:/\(/,end:/\)/}),l=e.inherit(e.APOS_STRING_MODE,{className:"string"}),r=e.inherit(e.QUOTE_STRING_MODE,{className:"string"}),g={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin://,relevance:10,contains:[c,r,l,i,{begin:/\[/,end:/\]/,contains:[{className:"meta",begin://,contains:[c,i,r,l]}]}]},e.COMMENT(//,{relevance:10}),{begin://,relevance:10},t,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:/)/,end:/>/,keywords:{name:"style"},contains:[g],starts:{end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:/)/,end:/>/,keywords:{name:"script"},contains:[g],starts:{end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/},{className:"tag",begin:n.concat(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name",begin:a,relevance:0,starts:g}]},{className:"tag",begin:n.concat(/<\//,n.lookahead(n.concat(a,/>/))),contains:[{className:"name",begin:a,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}e.exports=n}}]); \ No newline at end of file diff --git a/docs/js/index.980c39b5.js b/docs/js/index.980c39b5.js new file mode 100644 index 0000000..f0a0025 --- /dev/null +++ b/docs/js/index.980c39b5.js @@ -0,0 +1,9 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */(function(){var e={1970:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"close-icon",attrs:{viewBox:"0 0 14 14",themeId:"close"}},[t("path",{attrs:{d:"M12.73,0l1.27,1.27-5.74,5.73,5.72,5.72-1.27,1.27-5.72-5.72L1.28,13.99,.01,12.72,5.72,7.01,0,1.28,1.27,.01,6.99,5.73,12.73,0Z"}})])},o=[],i=n(9742),a={name:"CloseIcon",components:{SVGIcon:i.Z}},s=a,l=n(1001),c=(0,l.Z)(s,r,o,!1,null,null,null),u=c.exports},8785:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"inline-chevron-right-icon",attrs:{viewBox:"0 0 14 14",themeId:"inline-chevron-right"}},[t("path",{attrs:{d:"M2.964 1.366l0.649-0.76 7.426 6.343-7.423 6.445-0.655-0.755 6.545-5.683-6.542-5.59z"}})])},o=[],i=n(9742),a={name:"InlineChevronRightIcon",components:{SVGIcon:i.Z}},s=a,l=n(1001),c=(0,l.Z)(s,r,o,!1,null,null,null),u=c.exports},9742:function(e,t,n){"use strict";n.d(t,{Z:function(){return u}});var r=function(){var e=this,t=e._self._c;return t("svg",{staticClass:"svg-icon",attrs:{"aria-hidden":"true",xmlns:"http://www.w3.org/2000/svg","xmlns:xlink":"http://www.w3.org/1999/xlink"}},[e.themeOverrideURL?t("use",{attrs:{href:`${e.themeOverrideURL}#${e.themeId}`,width:"100%",height:"100%"}}):e._t("default")],2)},o=[],i=n(9089),a={name:"SVGIcon",props:{themeId:{type:String,required:!1},iconUrl:{type:String,default:null}},computed:{themeOverrideURL:({iconUrl:e,themeId:t})=>e||(0,i.$8)(["theme","icons",t],void 0)}},s=a,l=n(1001),c=(0,l.Z)(s,r,o,!1,null,"3434f4d2",null),u=c.exports},4830:function(e,t,n){"use strict";n(647);var r=n(144),o=n(7152),i=n(8345),a=function(){var e=this,t=e._self._c;return t("div",{class:{fromkeyboard:e.fromKeyboard,hascustomheader:e.hasCustomHeader},attrs:{id:"app"}},[t("div",{attrs:{id:e.AppTopID}}),e.isTargetIDE?e._e():t("a",{attrs:{href:"#main",id:"skip-nav"}},[e._v(e._s(e.$t("accessibility.skip-navigation")))]),e._t("header",(function(){return[e.enablei18n?t("SuggestLang"):e._e(),e.hasCustomHeader?t("custom-header",{attrs:{"data-color-scheme":e.preferredColorScheme}}):e._e()]}),{isTargetIDE:e.isTargetIDE}),t("div",{attrs:{id:e.baseNavStickyAnchorId}}),t("InitialLoadingPlaceholder"),e._t("default",(function(){return[t("router-view",{staticClass:"router-content"}),e.hasCustomFooter?t("custom-footer",{attrs:{"data-color-scheme":e.preferredColorScheme}}):e.isTargetIDE?e._e():t("Footer",{scopedSlots:e._u([{key:"default",fn:function({className:n}){return[e.enablei18n?t("div",{class:n},[t("LocaleSelector")],1):e._e()]}}])})]}),{isTargetIDE:e.isTargetIDE}),e._t("footer",null,{isTargetIDE:e.isTargetIDE})],2)},s=[],l=n(4030),c=n(9804),u=function(){var e=this,t=e._self._c;return t("footer",{staticClass:"footer"},[t("div",{staticClass:"row"},[t("ColorSchemeToggle")],1),e._t("default",null,{className:"row"})],2)},d=[],m=function(){var e=this,t=e._self._c;return t("fieldset",{staticClass:"color-scheme-toggle",attrs:{role:"radiogroup"}},[t("legend",{staticClass:"visuallyhidden"},[e._v(e._s(e.$t("color-scheme.select")))]),e._l(e.options,(function(n){return t("label",{key:n},[t("input",{attrs:{type:"radio"},domProps:{checked:n==e.preferredColorScheme,value:n},on:{input:e.setPreferredColorScheme}}),t("div",{staticClass:"text"},[e._v(e._s(e.$t(`color-scheme.${n}`)))])])}))],2)},p=[],h={name:"ColorSchemeToggle",data:()=>({appState:l["default"].state}),computed:{options:({supportsAutoColorScheme:e})=>[c.Z.light,c.Z.dark,...e?[c.Z.auto]:[]],preferredColorScheme:({appState:e})=>e.preferredColorScheme,supportsAutoColorScheme:({appState:e})=>e.supportsAutoColorScheme},methods:{setPreferredColorScheme:e=>{l["default"].setPreferredColorScheme(e.target.value)}},watch:{preferredColorScheme:{immediate:!0,handler(e){document.body.dataset.colorScheme=e}}}},f=h,g=n(1001),v=(0,g.Z)(f,m,p,!1,null,"0c0360ce",null),b=v.exports,w={name:"Footer",components:{ColorSchemeToggle:b}},y=w,S=(0,g.Z)(y,u,d,!1,null,"f1d65b2a",null),C=S.exports,_=function(){var e=this,t=e._self._c;return e.loaded?e._e():t("div",{staticClass:"InitialLoadingPlaceholder",attrs:{id:"loading-placeholder"}})},E=[],k={name:"InitialLoadingPlaceholder",data(){return{loaded:!1}},created(){const e=()=>{this.loaded=!0};this.$router.onReady(e,e)}},A=k,P=(0,g.Z)(A,_,E,!1,null,"35c356b6",null),L=P.exports,T=n(1716),j=n(9089);function x(e,t){return e&&"object"===typeof e&&Object.prototype.hasOwnProperty.call(e,t)&&"string"===typeof e[t]}function I(e,t,n,r){if(!t||"object"!==typeof t||r&&(x(t,"light")||x(t,"dark"))){let o=t;if(x(t,r)&&(o=t[r]),"object"===typeof o)return;n[e]=o}else Object.entries(t).forEach((([t,o])=>{const i=[e,t].join("-");I(i,o,n,r)}))}function N(e,t="light"){const n={},r=e||{};return I("-",r,n,t),n}var $=n(2717),O=function(){var e=this,t=e._self._c;return e.displaySuggestLang?t("div",{staticClass:"suggest-lang"},[t("div",{staticClass:"suggest-lang__wrapper"},[t("router-link",{staticClass:"suggest-lang__link",attrs:{to:e.getLocaleParam(e.preferredLocale),lang:e.getCodeForSlug(e.preferredLocale)},nativeOn:{click:function(t){return e.setPreferredLocale(e.preferredLocale)}}},[e._v(e._s(e.$i18n.messages[e.preferredLocale]["view-in"])),t("InlineChevronRightIcon",{staticClass:"icon-inline"})],1),t("div",{staticClass:"suggest-lang__close-icon-wrapper"},[t("button",{staticClass:"suggest-lang__close-icon-button",attrs:{"aria-label":e.$t("continue-viewing")},on:{click:function(t){return e.setPreferredLocale(e.$i18n.locale)}}},[t("CloseIcon",{staticClass:"icon-inline"})],1)])],1)]):e._e()},R=[],D=n(8785),Z=n(1970),q=n(2412),U=n(9030),V={name:"SuggestLang",components:{InlineChevronRightIcon:D.Z,CloseIcon:Z.Z},computed:{preferredLocale:()=>{const e=l["default"].state.preferredLocale;if(e)return e;const t=q.find((e=>{const t=e.code.split("-")[0],n=window.navigator.language.split("-")[0];return n===t}));return t?t.slug:null},displaySuggestLang:({preferredLocale:e,$i18n:t})=>e&&t.locale!==e},methods:{setPreferredLocale:e=>{l["default"].setPreferredLocale(e)},getCodeForSlug:U.dZ,getLocaleParam:U.KP}},B=V,M=(0,g.Z)(B,O,R,!1,null,"c2dca0ae",null),H=M.exports,W=function(){var e=this,t=e._self._c;return t("div",{staticClass:"locale-selector"},[t("select",{attrs:{"aria-label":e.$t("select-language")},domProps:{value:e.$i18n.locale},on:{change:e.updateRouter}},e._l(e.locales,(function({slug:n,name:r,code:o}){return t("option",{key:n,attrs:{lang:o},domProps:{value:n}},[e._v(" "+e._s(r)+" ")])})),0),t("ChevronThickIcon",{staticClass:"icon-inline"})],1)},F=[],J=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"chevron-thick-icon",attrs:{viewBox:"0 0 14 10.5",themeId:"chevron-thick"}},[t("path",{attrs:{d:"M12.43,0l1.57,1.22L7,10.5,0,1.23,1.58,0,7,7,12.43,0Z"}})])},K=[],G=n(9742),z={name:"ChevronThickIcon",components:{SVGIcon:G.Z}},X=z,Y=(0,g.Z)(X,J,K,!1,null,null,null),Q=Y.exports,ee={name:"LocaleSelector",components:{ChevronThickIcon:Q},methods:{updateRouter({target:{value:e}}){this.$router.push((0,U.KP)(e)),l["default"].setPreferredLocale(e),(0,U.jk)(e,this)}},computed:{availableLocales:()=>l["default"].state.availableLocales,locales:({availableLocales:e})=>q.filter((({code:t})=>e.includes(t)))}},te=ee,ne=(0,g.Z)(te,W,F,!1,null,"d21858a2",null),re=ne.exports,oe={name:"CoreApp",components:{Footer:C,InitialLoadingPlaceholder:L,SuggestLang:H,LocaleSelector:re},provide(){return{isTargetIDE:this.isTargetIDE,performanceMetricsEnabled:"true"==={NODE_ENV:"production",VUE_APP_TITLE:"Documentation",BASE_URL:"{{BASE_PATH}}/"}.VUE_APP_PERFORMANCE_ENABLED}},data(){return{AppTopID:$.$,appState:l["default"].state,fromKeyboard:!1,isTargetIDE:"ide"==={NODE_ENV:"production",VUE_APP_TITLE:"Documentation",BASE_URL:"{{BASE_PATH}}/"}.VUE_APP_TARGET,themeSettings:j.S3,baseNavStickyAnchorId:T.EA}},computed:{currentColorScheme:({appState:e})=>e.systemColorScheme,preferredColorScheme:({appState:e})=>e.preferredColorScheme,availableLocales:({appState:e})=>e.availableLocales,CSSCustomProperties:({currentColorScheme:e,preferredColorScheme:t,themeSettings:n})=>N(n.theme,t===c.Z.auto?e:t),hasCustomHeader:()=>!!window.customElements.get("custom-header"),hasCustomFooter:()=>!!window.customElements.get("custom-footer"),enablei18n:({availableLocales:e})=>(0,j.$8)(["features","docs","i18n","enable"],!1)&&e.length>1},props:{enableThemeSettings:{type:Boolean,default:!0}},watch:{CSSCustomProperties:{immediate:!0,handler(e){this.detachStylesFromRoot(e),this.attachStylesToRoot(e)}}},async created(){window.addEventListener("keydown",this.onKeyDown),this.$bridge.on("navigation",this.handleNavigationRequest),this.enableThemeSettings&&Object.assign(this.themeSettings,await(0,j.Kx)()),window.addEventListener("pageshow",this.syncPreferredColorScheme),this.$once("hook:beforeDestroy",(()=>{window.removeEventListener("pageshow",this.syncPreferredColorScheme)}))},mounted(){(document.querySelector(".footer-current-year")||{}).innerText=(new Date).getFullYear(),this.attachColorSchemeListeners()},beforeDestroy(){this.fromKeyboard?window.removeEventListener("mousedown",this.onMouseDown):window.removeEventListener("keydown",this.onKeyDown),this.$bridge.off("navigation",this.handleNavigationRequest),this.detachStylesFromRoot(this.CSSCustomProperties)},methods:{onKeyDown(){this.fromKeyboard=!0,window.addEventListener("mousedown",this.onMouseDown),window.removeEventListener("keydown",this.onKeyDown)},onMouseDown(){this.fromKeyboard=!1,window.addEventListener("keydown",this.onKeyDown),window.removeEventListener("mousedown",this.onMouseDown)},handleNavigationRequest(e){this.$router.push(e)},attachColorSchemeListeners(){if(!window.matchMedia)return;const e=window.matchMedia("(prefers-color-scheme: dark)");e.addListener(this.onColorSchemePreferenceChange),this.$once("hook:beforeDestroy",(()=>{e.removeListener(this.onColorSchemePreferenceChange)})),this.onColorSchemePreferenceChange(e)},onColorSchemePreferenceChange({matches:e}){const t=e?c.Z.dark:c.Z.light;l["default"].setSystemColorScheme(t)},attachStylesToRoot(e){const t=document.body;Object.entries(e).filter((([,e])=>Boolean(e))).forEach((([e,n])=>{t.style.setProperty(e,n)}))},detachStylesFromRoot(e){const t=document.body;Object.entries(e).forEach((([e])=>{t.style.removeProperty(e)}))},syncPreferredColorScheme(){l["default"].syncPreferredColorScheme()}}},ie=oe,ae=(0,g.Z)(ie,a,s,!1,null,"7420dfd2",null),se=ae.exports;class le{constructor(){this.$send=()=>{}}send(e){this.$send(e)}}class ce{constructor(){const{webkit:{messageHandlers:{bridge:e={}}={}}={}}=window;this.bridge=e;const{postMessage:t=(()=>{})}=e;this.$send=t.bind(e)}send(e){this.$send(e)}}class ue{constructor(e=new le){this.backend=e,this.listeners={}}send(e){this.backend.send(e)}receive(e){this.emit(e.type,e.data)}emit(e,t){this.listeners[e]&&this.listeners[e].forEach((e=>e(t)))}on(e,t){this.listeners[e]||(this.listeners[e]=new Set),this.listeners[e].add(t)}off(e,t){this.listeners[e]&&this.listeners[e].delete(t)}}var de={install(e,t){let n;n=t.performanceMetricsEnabled||"ide"===t.appTarget?new ce:new le,e.prototype.$bridge=new ue(n)}};function me(e){return`custom-${e}`}function pe(e){return class extends HTMLElement{constructor(){super();const t=this.attachShadow({mode:"open"}),n=e.content.cloneNode(!0);t.appendChild(n)}}}function he(e){const t=me(e),n=document.getElementById(t);n&&window.customElements.define(t,pe(n))}function fe(e,t={names:["header","footer"]}){const{names:n}=t;e.config.ignoredElements=/^custom-/,n.forEach(he)}function ge(e,t){const{value:n=!1}=t;e.style.display=n?"none":""}var ve={hide:ge};function be(e,{performanceMetrics:t=!1}={}){e.config.productionTip=!1,e.use(fe),e.directive("hide",ve.hide),e.use(de,{appTarget:{NODE_ENV:"production",VUE_APP_TITLE:"Documentation",BASE_URL:"{{BASE_PATH}}/"}.VUE_APP_TARGET,performanceMetricsEnabled:t}),window.bridge=e.prototype.$bridge,e.config.performance=t}var we=n(4589),ye=n(5381),Se=n(5657),Ce=n(3208),_e=n(2449);const Ee=10;function ke(e){const{name:t}=e,n=t.includes(we.J_);return n?Ee:0}function Ae(){const{location:e}=window;return e.pathname+e.search+e.hash}function Pe(){const e=Math.max(document.documentElement.clientWidth||0,window.innerWidth||0);return ePromise.all([n.e(866),n.e(843)]).then(n.bind(n,5199))},{path:"/tutorials/:id/*",name:"topic",component:()=>Promise.all([n.e(866),n.e(842),n.e(162)]).then(n.bind(n,4104))},{path:"/documentation/*",name:we.J_,component:()=>Promise.all([n.e(866),n.e(104),n.e(842),n.e(982)]).then(n.bind(n,8590))},{path:"*",name:we.vL,component:Ke},{path:"*",name:we.Rp,component:Be}];const ze=[{pathPrefix:"/:locale?",nameSuffix:"-locale"}];function Xe(e,t=[],n=ze){return n.reduce(((n,r)=>n.concat(e.filter((e=>!t.includes(e.name))).map((e=>({...e,path:r.pathPrefix+e.path,name:e.name+r.nameSuffix}))))),[])}const Ye=[...Ge,...Xe(Ge,[we.vL,we.Rp])];function Qe(e={}){const t=new i.Z({mode:"history",base:j.FH,scrollBehavior:Le,...e,routes:e.routes||Ye});return t.onReady((()=>{"scrollRestoration"in window.history&&(window.history.scrollRestoration="manual"),Te()})),"ide"!=={NODE_ENV:"production",VUE_APP_TITLE:"Documentation",BASE_URL:"{{BASE_PATH}}/"}.VUE_APP_TARGET&&t.onError((e=>{const{route:n={path:"/"}}=e;t.replace({name:"server-error",params:[n.path]})})),window.addEventListener("unload",je),t}var et=n(7788);function tt(e=et){const{defaultLocale:t,messages:n,dateTimeFormats:r={}}=e,i=new o.Z({dateTimeFormats:r,locale:t,fallbackLocale:t,messages:n});return i}r["default"].use(be),r["default"].use(i.Z),r["default"].use(o.Z),document.documentElement.classList.remove("no-js"),new r["default"]({router:Qe(),render:e=>e(se),i18n:tt()}).$mount("#app")},2717:function(e,t,n){"use strict";n.d(t,{$:function(){return r}});const r="app-top"},9804:function(e,t){"use strict";t["Z"]={auto:"auto",dark:"dark",light:"light"}},1265:function(e,t){"use strict";t["Z"]={eager:"eager",lazy:"lazy"}},1716:function(e,t,n){"use strict";n.d(t,{EA:function(){return i},L$:function(){return o},MenuLinkModifierClasses:function(){return s},RS:function(){return r},Yj:function(){return a}});const r=52,o=48,i="nav-sticky-anchor",a="nav-open-navigator",s={noClose:"noclose"}},4589:function(e,t,n){"use strict";n.d(t,{J_:function(){return i},Rp:function(){return o},vL:function(){return r}});const r="not-found",o="server-error",i="documentation-topic"},7788:function(e,t,n){"use strict";n.r(t),n.d(t,{defaultLocale:function(){return s},messages:function(){return l}});var r=JSON.parse('{"view-in":"View in English","continue-viewing":"Continue viewing in English","language":"Language","video":{"title":"Video","replay":"Replay","play":"Play","pause":"Pause","watch":"Watch intro video","description":"Content description: {alt}","custom-controls":"Video with custom controls."},"tutorials":{"title":"Tutorial | Tutorials","step":"Step {number}","submit":"Submit","next":"Next","preview":{"title":"No Preview | Preview | Previews","no-preview-available-step":"No preview available for this step."},"nav":{"chapters":"Chapters","current":"Current {thing}"},"assessment":{"check-your-understanding":"Check Your Understanding","success-message":"Great job, you\'ve answered all the questions for this tutorial.","answer-result":"Answer {answer} is {result}","correct":"correct","incorrect":"incorrect","next-question":"Next question","legend":"Possible answers"},"project-files":"Project files","estimated-time":"Estimated Time","sections":{"chapter":"Chapter {number}"},"question-of":"Question {index} of {total}","section-of":"{number} of {total}","overriding-title":"{newTitle} with {title}","time":{"format":"{number} {minutes}","minutes":{"full":"minute | minutes | {count} minutes","short":"min | mins"},"hours":{"full":"hour | hours"}}},"documentation":{"title":"Documentation","nav":{"breadcrumbs":"Breadcrumbs","menu":"Menu","open-menu":"Open Menu","close-menu":"Close Menu"},"current-page":"Current page is {title}","card":{"learn-more":"Learn More","read-article":"Read article","start-tutorial":"Start tutorial","view-api":"View API collection","view-symbol":"View symbol","view-sample-code":"View sample code"},"view-more":"View more"},"declarations":{"hide-other-declarations":"Hide other declarations","show-all-declarations":"Show all declarations"},"aside-kind":{"beta":"Beta","experiment":"Experiment","important":"Important","note":"Note","tip":"Tip","warning":"Warning","deprecated":"Deprecated"},"change-type":{"added":"Added","modified":"Modified","deprecated":"Deprecated"},"verbs":{"hide":"Hide","show":"Show","close":"Close"},"sections":{"attributes":"Attributes","title":"Section {number}","on-this-page":"On this page","topics":"Topics","default-implementations":"Default Implementations","relationships":"Relationships","see-also":"See Also","declaration":"Declaration","details":"Details","parameters":"Parameters","possible-values":"Possible Values","parts":"Parts","availability":"Availability","resources":"Resources"},"metadata":{"details":{"name":"Name","key":"Key","type":"Type"},"beta":{"legal":"This documentation refers to beta software and may be changed.","software":"Beta Software"},"default-implementation":"Default implementation provided. | Default implementations provided."},"availability":{"introduced-and-deprecated":"Introduced in {name} {introducedAt} and deprecated in {name} {deprecatedAt}","available-on":"Available on {name} {introducedAt} and later"},"more":"More","less":"Less","api-reference":"API Reference","filter":{"title":"Filter","search":"Search","search-symbols":"Search symbols in {technology}","suggested-tags":"Suggested tag | Suggested tags","selected-tags":"Selected tag | Selected tags","add-tag":"Add tag","tag-select-remove":"Tag. Select to remove from list.","navigate":"To navigate the symbols, press Up Arrow, Down Arrow, Left Arrow or Right Arrow","siblings-label":"{number-siblings} of {total-siblings} symbols inside {parent-siblings}","parent-label":"{number-siblings} of {total-siblings} symbols inside {parent-siblings} containing one symbol | {number-siblings} of {total-siblings} symbols inside {parent-siblings} containing {number-parent} symbols","reset-filter":"Reset Filter","tags":{"sample-code":"Sample Code","tutorials":"Tutorials","articles":"Articles","web-service-endpoints":"Web Service Endpoints","hide-deprecated":"Hide Deprecated"}},"navigator":{"title":"Documentation Navigator","open-navigator":"Open Documentation Navigator","close-navigator":"Close Documentation Navigator","no-results":"No results found.","no-children":"No data available.","error-fetching":"There was an error fetching the data.","items-found":"No items were found | 1 item was found | {number} items were found. Tab back to navigate through them.","navigator-is":"Navigator is {state}","state":{"loading":"loading","ready":"ready"}},"tab":{"request":"Request","response":"Response"},"required":"Required","parameters":{"default":"Default","minimum":"Minimum","maximum":"Maximum","possible-types":"Type | Possible types","possible-values":"Value | Possible Values"},"content-type":"Content-Type: {value}","read-only":"Read-only","error":{"unknown":"An unknown error occurred.","image":"Image failed to load","not-found":"The page you\'re looking for can\'t be found."},"color-scheme":{"select":"Select a color scheme preference","auto":"Auto","dark":"Dark","light":"Light"},"accessibility":{"strike":{"start":"start of stricken text","end":"end of stricken text"},"code":{"start":"start of code block","end":"end of code block"},"skip-navigation":"Skip Navigation","in-page-link":"in page link"},"select-language":"Select the language for this page","icons":{"clear":"Clear","web-service-endpoint":"Web Service Endpoint","search":"Search"},"formats":{"parenthesis":"({content})","colon":"{content}: "},"quicknav":{"button":{"label":"Open Quick Navigation","title":"Click or type / for quick navigation"},"preview-unavailable":"Preview unavailable"},"mentioned-in":"Mentioned in","pager":{"roledescription":"content slider","page":{"label":"content page {index} of {count}"},"control":{"navigate-previous":"navigate to previous page of content","navigate-next":"navigate to next page of content"}},"links-grid":{"label":"grid of links"}}'),o=JSON.parse('{"view-in":"以中文查看","continue-viewing":"继续以中文查看","language":"语言","video":{"title":"视频","replay":"重新播放","play":"播放","pause":"暂停","watch":"观看介绍视频","description":"此视频的内容描述:{alt}","custom-controls":"可用自定义控件如下"},"tutorials":{"title":"教程","step":"第 {number} 步","submit":"提交","next":"下一步","preview":{"title":"无预览 | 预览","no-preview-available-step":"这一步没有预览。"},"nav":{"chapters":"章节","current":"当前{thing}"},"assessment":{"check-your-understanding":"检查你的理解程度","success-message":"很棒,你回答了此教程的所有问题。","answer-result":"答案 {answer} 是 {result}","correct":"正确","incorrect":"错误","next-question":"下一个问题","legend":"可能的答案"},"project-files":"项目文件","estimated-time":"预计时间","sections":{"chapter":"第 {number} 章"},"question-of":"第 {index} 个问题(共 {total} 个)","section-of":"{number}/{total}","overriding-title":"{newTitle}{title}","time":{"format":"{number} {minutes}","minutes":{"full":"分钟 | {count} 分钟","short":"分钟"},"hours":{"full":"小时"}}},"documentation":{"title":"文档","nav":{"breadcrumbs":"面包屑导航","menu":"菜单","open-menu":"打开菜单","close-menu":"关闭菜单"},"current-page":"当前页面为:{title}","card":{"learn-more":"进一步了解","read-article":"阅读文章","start-tutorial":"开始教程","view-api":"查看 API 集合","view-symbol":"查看符号","view-sample-code":"查看示例代码"},"view-more":"查看更多"},"declarations":{"hide-other-declarations":"隐藏其他声明","show-all-declarations":"显示所有声明"},"aside-kind":{"beta":"Beta 版","experiment":"试验","important":"重要事项","note":"注","tip":"提示","warning":"警告","deprecated":"已弃用"},"change-type":{"added":"已添加","modified":"已修改","deprecated":"已弃用"},"verbs":{"hide":"隐藏","show":"显示","close":"关闭"},"sections":{"title":"第 {number} 部分","on-this-page":"在此页面上","topics":"主题","default-implementations":"默认实现","relationships":"关系","see-also":"另请参阅","declaration":"声明","details":"详细信息","parameters":"参数","possible-values":"可能值","parts":"部件","availability":"可用性","resources":"资源"},"metadata":{"details":{"name":"名称","key":"密钥","type":"类型"},"beta":{"legal":"此文档涉及 Beta 版软件且可能会改动。","software":"Beta 版软件"},"default-implementation":"提供默认实现。| 提供默认实现方法。"},"availability":{"introduced-and-deprecated":"{name} {introducedAt} 中引入,{name} {deprecatedAt} 中弃用","available-on":"{name} {introducedAt} 及更高版本中可用"},"more":"更多","less":"更少","api-reference":"API 参考","filter":{"title":"过滤","search":"搜索","search-symbols":"在 {technology} 搜索符号","suggested-tags":"建议标签","selected-tags":"所选标签","add-tag":"添加标签","tag-select-remove":"标签。选择以从列表中移除。","navigate":"若要导航符号,请按下上箭头、下箭头、左箭头或右箭头。","siblings-label":"{parent-siblings} 内含 {number-siblings} 个符号(共 {total-siblings} 个)","parent-label":"{parent-siblings} 内含 {number-siblings} 个符号(共 {total-siblings} 个)包含一个符号 | {parent-siblings} 内含 {number-siblings} 个符号(共 {total-siblings} 个)包含 {number-parent} 个符号","reset-filter":"还原过滤条件","tags":{"sample-code":"示例代码","tutorials":"教程","articles":"文章","web-service-endpoints":"网络服务端点","hide-deprecated":"隐藏已弃用"}},"navigator":{"title":"文档导航器","open-navigator":"打开文档导航器","close-navigator":"关闭文档导航器","no-results":"未找到结果。","no-children":"无可用数据。","error-fetching":"获取数据时出错。","items-found":"未找到任何项目 | 找到 1 个项目 | 找到 {number} 个项目。按下 Tab 键导航。","navigator-is":"导航器{state}","state":{"loading":"正在载入","ready":"准备就绪"}},"tab":{"request":"请求","response":"回复"},"required":"必需","parameters":{"default":"默认","minimum":"最小值","maximum":"最大值","possible-types":"类型 | 可能类型","possible-values":"值 | 可能值"},"content-type":"内容类型:{value}","read-only":"只读","error":{"unknown":"出现未知错误。","image":"图像无法载入","not-found":"找不到你所查找的页面。"},"color-scheme":{"select":"选择首选颜色方案","auto":"自动","dark":"深色","light":"浅色"},"accessibility":{"strike":{"start":"删除线文本开始","end":"删除线文本结束"},"code":{"start":"代码块开头","end":"代码块结尾"},"skip-navigation":"跳过导航","in-page-link":"在页面链接中"},"select-language":"选择此页面的语言","icons":{"clear":"清除","web-service-endpoint":"网络服务端点","search":"搜索"},"formats":{"parenthesis":"({content})","colon":"{content}: "},"quicknav":{"button":{"label":"打开快速导航","title":"点按或键入 / 进行快速导航"},"preview-unavailable":"预览不可用"},"mentioned-in":"提及于","pager":{"roledescription":"内容滑块","page":{"label":"第 {index}/{count} 个内容页面"},"control":{"navigate-previous":"导览至上个内容页面","navigate-next":"导览至下个内容页面"}},"links-grid":{"label":"链接网格"}}'),i=JSON.parse('{"view-in":"日本語で表示","continue-viewing":"日本語で表示を続ける","language":"言語","video":{"title":"ビデオ","replay":"リプレイ","play":"再生","pause":"一時停止","watch":"概要のビデオを観る","description":"このビデオコンテンツの説明: {alt}","custom-controls":"以下のカスタムコントロールを使用できます"},"tutorials":{"title":"チュートリアル | チュートリアル","step":"手順{number}","submit":"送信","next":"次へ","preview":{"title":"プレビューなし | プレビュー | プレビュー","no-preview-available-step":"この手順では利用可能なプレビューがありません。"},"nav":{"chapters":"章","current":"現在の{thing}"},"assessment":{"check-your-understanding":"理解度を確認する","success-message":"よくできました。このチュートリアルの問題にすべて解答しました。","answer-result":"解答{answer}は{result}です","correct":"正解","incorrect":"不正解","next-question":"次の問題","legend":"解答例"},"project-files":"プロジェクトファイル","estimated-time":"予測時間","sections":{"chapter":"{number}章"},"question-of":"{total}問中の{index}問","section-of":"{total}件中の{number}件","overriding-title":"{title}の{newTitle}","time":{"format":"{number} {minutes}","minutes":{"full":"分 | 分 | {count}分","short":"分 | 分"},"hours":{"full":"時間 | 時間"}}},"documentation":{"title":"ドキュメント","nav":{"breadcrumbs":"パンくずリスト","menu":"メニュー","open-menu":"メニューを開く","close-menu":"メニューを閉じる"},"current-page":"現在のページは{title}です","card":{"learn-more":"詳しい情報","read-article":"記事を読む","start-tutorial":"チュートリアルを開始","view-api":"APIのコレクションを表示","view-symbol":"記号を表示","view-sample-code":"サンプルコードを表示"},"view-more":"さらに表示"},"declarations":{"hide-other-declarations":"ほかの宣言を非表示","show-all-declarations":"すべての宣言を表示"},"aside-kind":{"beta":"ベータ版","experiment":"試験運用版","important":"重要","note":"注意","tip":"ヒント","warning":"警告","deprecated":"非推奨"},"change-type":{"added":"追加","modified":"変更","deprecated":"非推奨"},"verbs":{"hide":"非表示","show":"表示","close":"閉じる"},"sections":{"title":"セクション{number}","on-this-page":"このページの内容","topics":"トピック","default-implementations":"デフォルト実装","relationships":"関連項目","see-also":"参照","declaration":"宣言","details":"詳細","parameters":"パラメータ","possible-values":"使用できる値","parts":"パーツ","availability":"利用可能","resources":"リソース"},"metadata":{"details":{"name":"名前","key":"キー","type":"タイプ"},"beta":{"legal":"このドキュメントはベータ版のソフトウェアのもので、変更される可能性があります。","software":"ベータ版ソフトウェア"},"default-implementation":"デフォルト実装あり。| デフォルト実装あり。"},"availability":{"introduced-and-deprecated":"{name} {introducedAt}で導入され、{name} {deprecatedAt}で非推奨になりました","available-on":"{name} {introducedAt}以降で使用できます"},"more":"さらに表示","less":"表示を減らす","api-reference":"APIリファレンス","filter":{"title":"フィルタ","search":"検索","search-symbols":"{technology}でシンボルを検索","suggested-tags":"提案されたタグ | 提案されたタグ","selected-tags":"選択したタグ | 選択したタグ","add-tag":"タグを追加","tag-select-remove":"タグ。選択してリストから削除します。","navigate":"シンボルを移動するには、上下左右の矢印キーを押します。","siblings-label":"{total-siblings}個中{number-siblings}個のシンボルが{parent-siblings}の中にあります","parent-label":"{total-siblings}個中{number-siblings}個のシンボルが1個のシンボルを含む{parent-siblings}の中にあります | {total-siblings}個中{number-siblings}個のシンボルが{number-parent}個のシンボルを含む{parent-siblings}の中にあります","reset-filter":"フィルタをリセット","tags":{"sample-code":"サンプルコード","tutorials":"チュートリアル","articles":"記事","web-service-endpoints":"Webサービスのエンドポイント","hide-deprecated":"非推奨の項目を非表示"}},"navigator":{"title":"ドキュメントナビゲータ","open-navigator":"ドキュメントナビゲータを開く","close-navigator":"ドキュメントナビゲータを閉じる","no-results":"結果が見つかりません。","no-children":"使用できるデータがありません。","error-fetching":"データを取得する際にエラーが起きました。","items-found":"項目が見つかりません | 1個の項目が見つかりました | {number}個の項目が見つかりましたTabキーを押すと項目をナビゲートできます。","navigator-is":"ナビゲータは{state}です","state":{"loading":"読み込み中","ready":"準備完了"}},"tab":{"request":"リクエスト","response":"レスポンス"},"required":"必須","parameters":{"default":"デフォルト","minimum":"最小","maximum":"最大","possible-types":"タイプ | 使用できるタイプ","possible-values":"値 | 使用できる値"},"content-type":"Content-Type: {value}","read-only":"読み出し専用","error":{"unknown":"原因不明のエラーが起きました。","image":"イメージを読み込めませんでした","not-found":"探しているページが見つかりません。"},"color-scheme":{"select":"カラースキーム環境設定を選択","auto":"自動","dark":"ダーク","light":"ライト"},"accessibility":{"strike":{"start":"取り消し線テキストの開始","end":"取り消し線テキストの終了"},"code":{"start":"コードブロックの開始","end":"コードブロックの終了"},"skip-navigation":"ナビゲーションをスキップ","in-page-link":"ページ内リンク"},"select-language":"このページの言語を選択","icons":{"clear":"消去","web-service-endpoint":"Webサービスのエンドポイント","search":"検索"},"formats":{"parenthesis":"({content})","colon":"{content}: "},"quicknav":{"button":{"label":"クイックナビゲーションを開く","title":"クリックするか「/」を入力すると素早く移動します"},"preview-unavailable":"プレビューできません"},"mentioned-in":"言及: ","pager":{"roledescription":"コンテンツスライダー","page":{"label":"コンテンツページ{index}/{count}"},"control":{"navigate-previous":"前のコンテンツページに移動","navigate-next":"次のコンテンツページに移動"}},"links-grid":{"label":"リンクのグリッド"}}'),a=JSON.parse('{"view-in":"한국어로 보기","continue-viewing":"한국어로 계속 보기","language":"언어","video":{"title":"비디오","replay":"다시 재생","play":"재생","pause":"일시 정지","watch":"소개 비디오 시청하기","description":"이 비디오의 콘텐츠 설명: {alt}","custom-controls":"사용자 설정 제어기는 아래에서 사용 가능"},"tutorials":{"title":"튜토리얼 | 튜토리얼","step":"{number}단계","submit":"제출","next":"다음","preview":{"title":"미리보기 없음 | 미리보기 | 미리보기","no-preview-available-step":"이 단계에서는 사용 가능한 미리보기가 없습니다."},"nav":{"chapters":"챕터","current":"현재 {thing}"},"assessment":{"check-your-understanding":"얼마나 이해했는지 확인하기","success-message":"잘 하셨습니다. 이 튜토리얼에 대한 모든 질문에 답하셨습니다.","answer-result":"답 {answer}은(는) {result}입니다.","correct":"정답","incorrect":"오답","next-question":"다음 질문","legend":"가능한 답"},"project-files":"프로젝트 파일","estimated-time":"예상 시간","sections":{"chapter":"{number}챕터"},"question-of":"총 {total}개 중 {index}번째 질문","section-of":"{total} 중 {number}","overriding-title":"{title}의 {newTitle}","time":{"format":"{number}{minutes}","minutes":{"full":"분 | 분 | {count}분","short":"분 | 분"},"hours":{"full":"시간 | 시간"}}},"documentation":{"title":"문서","nav":{"breadcrumbs":"브레드크럼","menu":"메뉴","open-menu":"메뉴 열기","close-menu":"메뉴 닫기"},"current-page":"현재 {title} 페이지","card":{"learn-more":"더 알아보기","read-article":"문서 읽기","start-tutorial":"튜토리얼 시작","view-api":"API 모음 보기","view-symbol":"기호 보기","view-sample-code":"샘플 코드 보기"},"view-more":"더 보기"},"declarations":{"hide-other-declarations":"다른 선언 가리기","show-all-declarations":"모든 선언 표시하기"},"aside-kind":{"beta":"베타","experiment":"실험","important":"중요","note":"참고","tip":"팁","warning":"경고","deprecated":"제거됨"},"change-type":{"added":"추가됨","modified":"수정됨","deprecated":"제거됨"},"verbs":{"hide":"가리기","show":"보기","close":"닫기"},"sections":{"title":"{number}섹션","on-this-page":"이 페이지에서","topics":"주제","default-implementations":"기본 구현","relationships":"관계","see-also":"추가 정보","declaration":"선언","details":"세부사항","parameters":"매개변수","possible-values":"가능한 값","parts":"파트","availability":"사용 가능 여부","resources":"리소스"},"metadata":{"details":{"name":"이름","key":"키","type":"유형"},"beta":{"legal":"이 문서는 베타 소프트웨어에 대해서 다루고 있으며, 변경될 수 있습니다.","software":"베타 소프트웨어"},"default-implementation":"기본 구현 제공됨. | 기본 구현 제공됨."},"availability":{"introduced-and-deprecated":"{name} {introducedAt}에서 소개되었고 {name} {deprecatedAt}에서 제거됨","available-on":"{name} {introducedAt} 이상에서 사용할 수 있음"},"more":"더 보기","less":"간략히","api-reference":"API 참조","filter":{"title":"필터","search":"검색","search-symbols":"{technology}에서 기호 찾기","suggested-tags":"권장 태그 | 권장 태그","selected-tags":"선택한 태그 | 선택한 태그","add-tag":"태그 추가","tag-select-remove":"태그. 목록에서 제거하려면 선택하십시오.","navigate":"기호를 탐색하려면 위쪽 화살표, 아래쪽 화살표, 왼쪽 화살표 또는 오른쪽 화살표를 누르십시오.","siblings-label":"{parent-siblings} 내의 총 {total-siblings}개의 기호 중 {number-siblings}개","parent-label":"한 개의 기호를 포함하는 {parent-siblings} 내의 총 {total-siblings}개의 기호 중 {number-siblings}개 | {number-parent}개의 기호를 포함하는 {parent-siblings} 내의 총 {total-siblings}개의 기호 중 {number-siblings}개","reset-filter":"필터 재설정","tags":{"sample-code":"샘플 코드","tutorials":"튜토리얼","articles":"문서","web-service-endpoints":"웹 서비스 엔드포인트","hide-deprecated":"제거된 항목 가리기"}},"navigator":{"title":"문서 탐색기","open-navigator":"문서 탐색기 열기","close-navigator":"문서 탐색기 닫기","no-results":"결과 찾을 수 없습니다.","no-children":"사용 가능한 데이터가 없습니다.","error-fetching":"데이터를 가져오는 동안 오류가 발생했습니다.","items-found":"항목을 찾을 수 없음 | 1개의 항목 발견됨 | {number}개의 항목이 발견됨. 다시 탭하여 탐색하십시오.","navigator-is":"내비게이터가 {state}입니다.","state":{"loading":"로드 중","ready":"준비 완료 상태"}},"tab":{"request":"요청","response":"응답"},"required":"필수 사항","parameters":{"default":"기본","minimum":"최소","maximum":"최대","possible-types":"유형 | 가능한 유형","possible-values":"값 | 가능한 값"},"content-type":"콘텐츠 유형: {value}","read-only":"읽기 전용","error":{"unknown":"알 수 없는 오류가 발생했습니다.","image":"이미지를 로드하는 데 실패함","not-found":"해당 페이지를 찾을 수 없습니다."},"color-scheme":{"select":"색상 모드 환경설정 선택","auto":"자동","dark":"다크","light":"라이트"},"accessibility":{"strike":{"start":"취소선이 그어진 텍스트 시작","end":"취소선이 그어진 텍스트 종료"},"code":{"start":"코드 블록 시작","end":"코드 블록 종료"},"skip-navigation":"탐색 건너뛰기","in-page-link":"페이지 링크에서"},"select-language":"이 페이지의 언어 선택","icons":{"clear":"지우기","web-service-endpoint":"웹 서비스 엔드포인트","search":"검색"},"formats":{"parenthesis":"({content})","colon":"{content}: "},"quicknav":{"button":{"label":"빠른 이동 열기","title":"클릭하거나 /를 입력하여 빠르게 이동"},"preview-unavailable":"미리보기 사용할 수 없음"},"mentioned-in":"다음에서 언급됨","pager":{"roledescription":"콘텐츠 슬라이더","page":{"label":"{count} 중 {index}번째 콘텐츠 페이지"},"control":{"navigate-previous":"콘텐츠의 이전 페이지로 이동","navigate-next":"콘텐츠의 다음 페이지로 이동"}},"links-grid":{"label":"링크의 격자"}}');const s="en-US",l={"en-US":r,"zh-CN":o,"ja-JP":i,"ko-KR":a}},4030:function(e,t,n){"use strict";var r=n(9804),o=n(1265),i=n(5394),a=n(2412);const s="undefined"!==typeof window.matchMedia&&[r.Z.light,r.Z.dark,"no-preference"].some((e=>window.matchMedia(`(prefers-color-scheme: ${e})`).matches)),l=s?r.Z.auto:r.Z.light;t["default"]={state:{imageLoadingStrategy:"ide"==={NODE_ENV:"production",VUE_APP_TITLE:"Documentation",BASE_URL:"{{BASE_PATH}}/"}.VUE_APP_TARGET?o.Z.eager:o.Z.lazy,preferredColorScheme:i.Z.preferredColorScheme||l,preferredLocale:i.Z.preferredLocale,supportsAutoColorScheme:s,systemColorScheme:r.Z.light,availableLocales:[]},reset(){this.state.imageLoadingStrategy="ide"==={NODE_ENV:"production",VUE_APP_TITLE:"Documentation",BASE_URL:"{{BASE_PATH}}/"}.VUE_APP_TARGET?o.Z.eager:o.Z.lazy,this.state.preferredColorScheme=i.Z.preferredColorScheme||l,this.state.supportsAutoColorScheme=s,this.state.systemColorScheme=r.Z.light},setImageLoadingStrategy(e){this.state.imageLoadingStrategy=e},setPreferredColorScheme(e){this.state.preferredColorScheme=e,i.Z.preferredColorScheme=e},setAllLocalesAreAvailable(){const e=a.map((e=>e.code));this.state.availableLocales=e},setAvailableLocales(e=[]){this.state.availableLocales=e},setPreferredLocale(e){this.state.preferredLocale=e,i.Z.preferredLocale=this.state.preferredLocale},setSystemColorScheme(e){this.state.systemColorScheme=e},syncPreferredColorScheme(){i.Z.preferredColorScheme&&i.Z.preferredColorScheme!==this.state.preferredColorScheme&&(this.state.preferredColorScheme=i.Z.preferredColorScheme)}}},5947:function(e,t,n){"use strict";function r(e){return e.reduce(((e,t)=>(t.traits.includes("dark")?e.dark.push(t):e.light.push(t),e)),{light:[],dark:[]})}function o(e){const t=["1x","2x","3x"];return t.reduce(((t,n)=>{const r=e.find((e=>e.traits.includes(n)));return r?t.concat({density:n,src:r.url,size:r.size}):t}),[])}function i(e){const t="/",n=new RegExp(`${t}+`,"g");return e.join(t).replace(n,t)}function a(e){const{baseUrl:t}=window,n=Array.isArray(e)?i(e):e;return n&&"string"===typeof n&&!n.startsWith(t)&&n.startsWith("/")?i([t,n]):n}function s(e){return e?e.startsWith("/")?e:`/${e}`:e}function l(e){return e?`url('${a(e)}')`:void 0}function c(e){return new Promise(((t,n)=>{const r=new Image;r.src=e,r.onerror=n,r.onload=()=>t({width:r.width,height:r.height})}))}n.d(t,{AH:function(){return a},Jf:function(){return s},RY:function(){return c},T8:function(){return d},XV:function(){return r},eZ:function(){return l},u:function(){return o}});const u={landscape:"landscape",portrait:"portrait",square:"square"};function d(e,t){return e&&t?et?u.landscape:u.square:null}},5381:function(e,t,n){"use strict";n.d(t,{L3:function(){return r},fr:function(){return s},kB:function(){return i},lU:function(){return o}});const r={xlarge:"xlarge",large:"large",medium:"medium",small:"small"},o={default:"default",nav:"nav"},i={[o.default]:{[r.xlarge]:{minWidth:1920,contentWidth:1536},[r.large]:{minWidth:1251,contentWidth:980},[r.medium]:{minWidth:736,maxWidth:1068,contentWidth:692},[r.small]:{minWidth:320,maxWidth:735,contentWidth:280}},[o.nav]:{[r.large]:{minWidth:1024},[r.medium]:{minWidth:768,maxWidth:1023},[r.small]:{minWidth:320,maxWidth:767}}},a={[r.small]:0,[r.medium]:1,[r.large]:2};function s(e,t){return a[e]>a[t]}},9030:function(e,t,n){"use strict";n.d(t,{KP:function(){return c},dZ:function(){return s},jk:function(){return u}});var r=n(2412),o=n(7788),i=n(3465);const a=r.reduce(((e,t)=>({...e,[t.slug]:t.code})),{});function s(e){return a[e]}function l(e){return!!a[e]}function c(e){return{params:{locale:e===o.defaultLocale?void 0:e}}}function u(e=o.defaultLocale,t={}){if(!l(e))return;t.$i18n.locale=e;const n=s(e);(0,i.e)(n)}},5657:function(e,t,n){"use strict";function r(e){let t=null,n=e-1;const r=new Promise((e=>{t=e}));return requestAnimationFrame((function e(){n-=1,n<=0?t():requestAnimationFrame(e)})),r}function o(e){return new Promise((t=>{setTimeout(t,e)}))}n.d(t,{J:function(){return r},X:function(){return o}})},3465:function(e,t,n){"use strict";n.d(t,{X:function(){return u},e:function(){return d}});var r=n(9089),o=n(2449);const i=(0,r.$8)(["meta","title"],"Documentation"),a=({title:e,description:t,url:n,currentLocale:r})=>[{name:"description",content:t},{property:"og:locale",content:r},{property:"og:site_name",content:i},{property:"og:type",content:"website"},{property:"og:title",content:e},{property:"og:description",content:t},{property:"og:url",content:n},{property:"og:image",content:(0,o.HH)("/developer-og.jpg")},{name:"twitter:image",content:(0,o.HH)("/developer-og-twitter.jpg")},{name:"twitter:card",content:"summary_large_image"},{name:"twitter:description",content:t},{name:"twitter:title",content:e},{name:"twitter:url",content:n}],s=e=>[e,i].filter(Boolean).join(" | "),l=e=>{const{content:t}=e,n=e.property?"property":"name",r=e[n],o=document.querySelector(`meta[${n}="${r}"]`);if(o&&t)o.setAttribute("content",t);else if(o&&!t)o.remove();else if(t){const t=document.createElement("meta");t.setAttribute(n,e[n]),t.setAttribute("content",e.content),document.getElementsByTagName("head")[0].appendChild(t)}},c=e=>{document.title=e};function u({title:e,description:t,url:n,currentLocale:r}){const o=s(e);c(o),a({title:o,description:t,url:n,currentLocale:r}).forEach((e=>l(e)))}function d(e){document.querySelector("html").setAttribute("lang",e)}},5394:function(e,t,n){"use strict";var r=n(7247);const o={preferredColorScheme:"developer.setting.preferredColorScheme",preferredLocale:"developer.setting.preferredLocale",preferredLanguage:"docs.setting.preferredLanguage"},i={preferredColorScheme:"docs.setting.preferredColorScheme"};t["Z"]=Object.defineProperties({},Object.keys(o).reduce(((e,t)=>({...e,[t]:{get:()=>{const e=i[t],n=r.mr.getItem(o[t]);return e?n||r.mr.getItem(e):n},set:e=>r.mr.setItem(o[t],e)}})),{}))},7247:function(e,t,n){"use strict";n.d(t,{mr:function(){return a},tO:function(){return l},y7:function(){return c}});const r="developer.setting.";function o(e=localStorage){return{getItem:t=>{try{return e.getItem(t)}catch(n){return null}},setItem:(t,n)=>{try{e.setItem(t,n)}catch(r){}},removeItem:t=>{try{e.removeItem(t)}catch(n){}}}}function i(e){return{get:(t,n)=>{const o=JSON.parse(e.getItem(r+t));return null!==o?o:n},set:(t,n)=>e.setItem(r+t,JSON.stringify(n)),remove:t=>e.removeItem(r+t)}}const a=o(window.localStorage),s=o(window.sessionStorage),l=i(a),c=i(s)},3208:function(e,t,n){"use strict";n.d(t,{HA:function(){return a},RL:function(){return u},Xv:function(){return s},ZQ:function(){return d},hr:function(){return c},id:function(){return m},sj:function(){return l}});n(8269);const r=/(?:\s+|[`"<>])/g,o=/^-+/,i=/["'&<>]/g;function a(e){return e.trim().replace(r,"-").replace(o,"").toLowerCase()}function s(e){const t=e=>({'"':""","'":"'","&":"&","<":"<",">":">"}[e]||e);return e.replace(i,t)}function l(e){return e.replace(/#(.*)/,((e,t)=>`#${CSS.escape(t)}`))}function c(e){return e.replace(/[.*+\-?^${}()|[\]\\]/g,"\\$&")}function u(e){let t,n;const r="\\s*",o=" ",i=e.trim(),a=i.length;if(!a)return o;const s=[];for(t=0;te.json())).catch((()=>({})))}const l=(e,t)=>r(i,e,t)},2449:function(e,t,n){"use strict";n.d(t,{Lp:function(){return s},Q2:function(){return a},WN:function(){return l},Ex:function(){return i},HH:function(){return c}});var r=n(5947),o={input:"input",tags:"tags"};function i(e={}){return Object.entries(e).reduce(((e,[t,n])=>n?e.concat(`${encodeURIComponent(t)}=${encodeURIComponent(n)}`):e),[]).join("&")}function a(e,{changes:t,language:n,context:r}={}){const[o,a]=e.split("#"),s=o.match(/\?.*/),l=i({changes:t,language:n,context:r}),c=s?"&":"?",u=a?o:e,d=l?`${c}${l}`:"",m=a?`#${a}`:"";return`${u}${d}${m}`}function s(e,t){const{query:{changes:n,[o.input]:r,[o.tags]:i,...a}={}}=e,{query:{changes:s,[o.input]:l,[o.tags]:c,...u}={}}=t;return e.name===t.name&&JSON.stringify({path:e.path,query:a})===JSON.stringify({path:t.path,query:u})}function l(e,t=window.location.href){return new URL((0,r.AH)(e),t)}function c(e,t){return l(e,t).href}},647:function(e,t,n){n.p=window.baseUrl},2412:function(e){"use strict";e.exports=JSON.parse('[{"code":"en-US","name":"English","slug":"en-US"},{"code":"zh-CN","name":"简体中文","slug":"zh-CN"},{"code":"ja-JP","name":"日本語","slug":"ja-JP"},{"code":"ko-KR","name":"한국어","slug":"ko-KR"}]')}},t={};function n(r){var o=t[r];if(void 0!==o)return o.exports;var i=t[r]={exports:{}};return e[r].call(i.exports,i,i.exports,n),i.exports}n.m=e,function(){var e=[];n.O=function(t,r,o,i){if(!r){var a=1/0;for(u=0;u=i)&&Object.keys(n.O).every((function(e){return n.O[e](r[l])}))?r.splice(l--,1):(s=!1,i0&&e[u-1][2]>i;u--)e[u]=e[u-1];e[u]=[r,o,i]}}(),function(){n.n=function(e){var t=e&&e.__esModule?function(){return e["default"]}:function(){return e};return n.d(t,{a:t}),t}}(),function(){var e,t=Object.getPrototypeOf?function(e){return Object.getPrototypeOf(e)}:function(e){return e.__proto__};n.t=function(r,o){if(1&o&&(r=this(r)),8&o)return r;if("object"===typeof r&&r){if(4&o&&r.__esModule)return r;if(16&o&&"function"===typeof r.then)return r}var i=Object.create(null);n.r(i);var a={};e=e||[null,t({}),t([]),t(t)];for(var s=2&o&&r;"object"==typeof s&&!~e.indexOf(s);s=t(s))Object.getOwnPropertyNames(s).forEach((function(e){a[e]=function(){return r[e]}}));return a["default"]=function(){return r},n.d(i,a),i}}(),function(){n.d=function(e,t){for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})}}(),function(){n.f={},n.e=function(e){return Promise.all(Object.keys(n.f).reduce((function(t,r){return n.f[r](e,t),t}),[]))}}(),function(){n.u=function(e){return"js/"+({82:"highlight-js-json-js",113:"highlight-js-markdown-js",133:"highlight-js-llvm-js",162:"topic",176:"highlight-js-shell-js",213:"highlight-js-diff-js",217:"highlight-js-custom-swift",392:"highlight-js-scss-js",393:"highlight-js-bash-js",435:"highlight-js-python-js",490:"highlight-js-xml-js",527:"highlight-js-swift-js",546:"highlight-js-c-js",596:"highlight-js-php-js",621:"highlight-js-cpp-js",623:"highlight-js-ruby-js",637:"highlight-js-objectivec-js",642:"highlight-js-custom-markdown",645:"highlight-js-perl-js",788:"highlight-js-java-js",814:"highlight-js-javascript-js",843:"tutorials-overview",864:"highlight-js-css-js",878:"highlight-js-http-js",982:"documentation-topic"}[e]||e)+"."+{82:"2a1856ba",104:"35ce41d9",113:"a2f456af",133:"26121771",162:"99467a55",176:"0ad5b20f",213:"4db9a783",217:"738731d1",337:"274a8ccc",392:"adcd11a2",393:"702f0c5c",435:"60354774",490:"0d78f903",527:"bdd5bff5",546:"063069d3",596:"c458ffa4",621:"458a9ae4",623:"7272231f",637:"74dea052",642:"78c9f6ed",645:"da6eda82",773:"03f3f776",788:"4fe21e94",814:"dfc9d16d",842:"bf5508df",843:"d4267041",864:"bfc4251f",866:"f57382ad",878:"f78e83c2",982:"71f67a4e"}[e]+".js"}}(),function(){n.miniCssF=function(e){return"css/"+({162:"topic",843:"tutorials-overview",982:"documentation-topic"}[e]||e)+"."+{162:"7d90b3b4",773:"47cca215",843:"ddd5b228",866:"60f074fd",982:"2a4050e5"}[e]+".css"}}(),function(){n.g=function(){if("object"===typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"===typeof window)return window}}()}(),function(){n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)}}(),function(){var e={},t="swift-docc-render:";n.l=function(r,o,i,a){if(e[r])e[r].push(o);else{var s,l;if(void 0!==i)for(var c=document.getElementsByTagName("script"),u=0;ue.anchor===t.anchor?{...t,visibility:e.visibility}:t))},updateBreakpoint(e){this.state.breakpoint=e},setReferences(e){this.state.references=e}},d=function(){var e=this,t=e._self._c;return t("div",{staticClass:"article"},[e.isTargetIDE?e._e():t("NavigationBar",{attrs:{chapters:e.hierarchy.modules,technology:e.metadata.category,topic:e.heroTitle||"",rootReference:e.hierarchy.reference,identifierUrl:e.identifierUrl}}),t("main",{attrs:{id:"main",tabindex:"0"}},[e._t("above-hero"),e._l(e.sections,(function(n,s){return t(e.componentFor(n),e._b({key:s,tag:"component"},"component",e.propsFor(n),!1))}))],2),t("PortalTarget",{attrs:{name:"modal-destination",multiple:""}})],1)},p=[],h=n(2433),m=n(4030),v=function(){var e=this,t=e._self._c;return t("NavBase",{attrs:{id:"nav","aria-label":e.technology,hasSolidBackground:""},scopedSlots:e._u([{key:"default",fn:function(){return[t("ReferenceUrlProvider",{attrs:{reference:e.rootReference},scopedSlots:e._u([{key:"default",fn:function({urlWithParams:n}){return[t("NavTitleContainer",{attrs:{to:n},scopedSlots:e._u([{key:"default",fn:function(){return[e._v(e._s(e.technology))]},proxy:!0},{key:"subhead",fn:function(){return[e._v(e._s(e.$tc("tutorials.title",2)))]},proxy:!0}],null,!0)})]}}])})]},proxy:!0},{key:"after-title",fn:function(){return[t("div",{staticClass:"separator"})]},proxy:!0},{key:"tray",fn:function(){return[t("div",{staticClass:"mobile-dropdown-container"},[t("MobileDropdown",{attrs:{options:e.chapters,sections:e.optionsForSections,currentOption:e.currentSection?e.currentSection.title:""},on:{"select-section":e.onSelectSection}})],1),t("div",{staticClass:"dropdown-container"},[t("PrimaryDropdown",{staticClass:"primary-dropdown",attrs:{options:e.chapters,currentOption:e.topic}}),t("ChevronIcon",{staticClass:"icon-inline"}),e.currentSection?t("SecondaryDropdown",{staticClass:"secondary-dropdown",attrs:{options:e.optionsForSections,currentOption:e.currentSection.title,sectionTracker:e.sectionIndicatorText},on:{"select-section":e.onSelectSection}}):e._e()],1),e._t("tray",null,{siblings:e.chapters.length+e.optionsForSections.length})]},proxy:!0}],null,!0)})},f=[],g=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"chevron-icon",attrs:{viewBox:"0 0 14 14",themeId:"chevron"}},[t("path",{attrs:{d:"M3.22 1.184l0.325-0.38 7.235 6.201-7.235 6.19-0.325-0.38 6.792-5.811-6.792-5.82z"}})])},y=[],C=n(9742),b={name:"ChevronIcon",components:{SVGIcon:C.Z}},_=b,w=n(1001),k=(0,w.Z)(_,g,y,!1,null,null,null),S=k.exports,x=n(2449),A=n(5953),T={name:"ReferenceUrlProvider",mixins:[A.Z],props:{reference:{type:String,required:!0}},computed:{resolvedReference:({references:e,reference:t})=>e[t]||{},url:({resolvedReference:e})=>e.url,title:({resolvedReference:e})=>e.title},render(){return this.$scopedSlots.default({url:this.url,urlWithParams:(0,x.Q2)(this.url,this.$route.query),title:this.title,reference:this.resolvedReference})}},I=T,$=(0,w.Z)(I,l,c,!1,null,null,null),N=$.exports,q=n(3704),P=n(2586),D=n(2573),Z=function(){var e=this,t=e._self._c;return t("NavMenuItems",{staticClass:"mobile-dropdown"},e._l(e.options,(function(n){return t("ReferenceUrlProvider",{key:n.reference,attrs:{reference:n.reference},scopedSlots:e._u([{key:"default",fn:function({title:s}){return[t("NavMenuItemBase",{staticClass:"chapter-list",attrs:{role:"group"}},[t("p",{staticClass:"chapter-name"},[e._v(e._s(s))]),t("ul",{staticClass:"tutorial-list"},e._l(n.projects,(function(n){return t("ReferenceUrlProvider",{key:n.reference,attrs:{reference:n.reference},scopedSlots:e._u([{key:"default",fn:function({url:n,urlWithParams:s,title:i}){return[t("li",{staticClass:"tutorial-list-item"},[t("router-link",{staticClass:"option tutorial",attrs:{to:s,value:i}},[e._v(" "+e._s(i)+" ")]),n===e.$route.path?t("ul",{staticClass:"section-list",attrs:{role:"listbox"}},e._l(e.sections,(function(n){return t("li",{key:n.title},[t("router-link",{class:e.classesFor(n),attrs:{to:{path:n.path,query:e.$route.query},value:n.title},nativeOn:{click:function(t){return e.onClick(n)}}},[e._v(" "+e._s(n.title)+" ")])],1)})),0):e._e()],1)]}}],null,!0)})})),1)])]}}],null,!0)})})),1)},R=[],O=n(535),L=n(2853),B={name:"MobileDropdown",components:{NavMenuItems:L.Z,NavMenuItemBase:O.Z,ReferenceUrlProvider:N},props:{options:{type:Array,required:!0},currentOption:{type:String,required:!0},sections:{type:Array,required:!1,default:()=>[]}},methods:{classesFor(e){return["option","section",{active:this.currentOption===e.title},this.depthClass(e)]},depthClass(e){const{depth:t=0}=e;return`depth${t}`},onClick(e){this.$emit("select-section",e.path)}}},M=B,F=(0,w.Z)(M,Z,R,!1,null,"2c27d339",null),V=F.exports,j=function(){var e=this,t=e._self._c;return t("DropdownCustom",{staticClass:"tutorial-dropdown",attrs:{value:e.currentOption,"aria-label":e.$t("tutorials.nav.current",{thing:e.$t("sections.title")}),isSmall:""},scopedSlots:e._u([{key:"toggle-post-content",fn:function(){return[t("span",{staticClass:"section-tracker"},[e._v(e._s(e.sectionTracker))])]},proxy:!0},{key:"default",fn:function({closeAndFocusToggler:n,contentClasses:s,navigateOverOptions:i,OptionClass:r,ActiveOptionClass:o}){return[t("ul",{staticClass:"options",class:s,attrs:{role:"listbox",tabindex:"0"}},e._l(e.options,(function(s){return t("router-link",{key:s.title,attrs:{to:{path:s.path,query:e.$route.query},custom:""},scopedSlots:e._u([{key:"default",fn:function({navigate:a}){return[t("li",{class:[r,{[o]:e.currentOption===s.title}],attrs:{value:s.title,"aria-selected":e.currentOption===s.title,"aria-current":e.ariaCurrent(s.title),tabindex:-1},on:{click:function(t){return e.setActive(s,a,n,t)},keydown:[function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")?null:e.setActive(s,a,n,t)},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"esc",27,t.key,["Esc","Escape"])?null:n.apply(null,arguments)},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"tab",9,t.key,"Tab")?null:n.apply(null,arguments)},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"down",40,t.key,["Down","ArrowDown"])?null:(t.preventDefault(),i(t,1))},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"up",38,t.key,["Up","ArrowUp"])?null:(t.preventDefault(),i(t,-1))}]}},[e._v(" "+e._s(s.title)+" ")])]}}],null,!0)})})),1)]}}])})},E=[],H=function(){var e=this,t=e._self._c;return t("BaseDropdown",{staticClass:"dropdown-custom",class:{[e.OpenedClass]:e.isOpen,"dropdown-small":e.isSmall},attrs:{value:e.value},scopedSlots:e._u([{key:"dropdown",fn:function({dropdownClasses:n}){return[t("span",{staticClass:"visuallyhidden",attrs:{id:`DropdownLabel_${e._uid}`}},[e._v(e._s(e.ariaLabel))]),t("button",{ref:"dropdownToggle",staticClass:"form-dropdown-toggle",class:n,attrs:{id:`DropdownToggle_${e._uid}`,"aria-labelledby":`DropdownLabel_${e._uid} DropdownToggle_${e._uid}`,"aria-expanded":e.isOpen?"true":"false","aria-haspopup":"true"},on:{click:e.toggleDropdown,keydown:[function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")?null:(t.preventDefault(),e.openDropdown.apply(null,arguments))},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"esc",27,t.key,["Esc","Escape"])?null:e.closeAndFocusToggler.apply(null,arguments)},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"down",40,t.key,["Down","ArrowDown"])?null:(t.preventDefault(),e.openDropdown.apply(null,arguments))},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"up",38,t.key,["Up","ArrowUp"])?null:(t.preventDefault(),e.openDropdown.apply(null,arguments))}]}},[t("span",{staticClass:"form-dropdown-title"},[e._v(e._s(e.value))]),e._t("toggle-post-content")],2)]}},{key:"eyebrow",fn:function(){return[e._t("eyebrow")]},proxy:!0},{key:"after",fn:function(){return[e._t("default",null,null,{value:e.value,isOpen:e.isOpen,contentClasses:["form-dropdown-content",{"is-open":e.isOpen}],closeDropdown:e.closeDropdown,onChangeAction:e.onChangeAction,closeAndFocusToggler:e.closeAndFocusToggler,navigateOverOptions:e.navigateOverOptions,OptionClass:e.OptionClass,ActiveOptionClass:e.ActiveOptionClass})]},proxy:!0}],null,!0)})},U=[],z=function(){var e=this,t=e._self._c;return t("div",{staticClass:"form-element"},[e._t("dropdown",(function(){return[t("select",e._b({directives:[{name:"model",rawName:"v-model",value:e.modelValue,expression:"modelValue"}],class:e.dropdownClasses,on:{change:function(t){var n=Array.prototype.filter.call(t.target.options,(function(e){return e.selected})).map((function(e){var t="_value"in e?e._value:e.value;return t}));e.modelValue=t.target.multiple?n:n[0]}}},"select",e.$attrs,!1),[e._t("default")],2)]}),{dropdownClasses:e.dropdownClasses,value:e.value}),t("InlineChevronDownIcon",{staticClass:"form-icon",attrs:{"aria-hidden":"true"}}),e.$slots.eyebrow?t("span",{staticClass:"form-label",attrs:{"aria-hidden":"true"}},[e._t("eyebrow")],2):e._e(),e._t("after")],2)},G=[],W=n(5151),Q={name:"BaseDropdown",inheritAttrs:!1,props:{value:{type:String,default:""}},components:{InlineChevronDownIcon:W.Z},computed:{modelValue:{get:({value:e})=>e,set(e){this.$emit("input",e)}},dropdownClasses({value:e}){return["form-dropdown",{"form-dropdown-selectnone":""===e,"no-eyebrow":!this.$slots.eyebrow}]}}},K=Q,X=(0,w.Z)(K,z,G,!1,null,"f934959a",null),Y=X.exports;const J="is-open",ee="option",te="option-active";var ne={name:"DropdownCustom",components:{BaseDropdown:Y},constants:{OpenedClass:J,OptionClass:ee,ActiveOptionClass:te},props:{value:{type:String,default:""},ariaLabel:{type:String,default:""},isSmall:{type:Boolean,default:!1}},data(){return{isOpen:!1,OpenedClass:J,OptionClass:ee,ActiveOptionClass:te}},mounted(){document.addEventListener("click",this.closeOnLoseFocus)},beforeDestroy(){document.removeEventListener("click",this.closeOnLoseFocus)},methods:{onChangeAction(e){this.$emit("input",e)},toggleDropdown(){this.isOpen?this.closeDropdown():this.openDropdown()},async closeAndFocusToggler(){this.closeDropdown(),await this.$nextTick(),this.$refs.dropdownToggle.focus({preventScroll:!0})},closeDropdown(){this.isOpen=!1,this.$emit("close")},openDropdown(){this.isOpen=!0,this.$emit("open"),this.focusActiveLink()},closeOnLoseFocus(e){!this.$el.contains(e.target)&&this.isOpen&&this.closeDropdown()},navigateOverOptions({target:e},t){const n=this.$el.querySelectorAll(`.${ee}`),s=Array.from(n),i=s.indexOf(e),r=s[i+t];r&&r.focus({preventScroll:!0})},async focusActiveLink(){const e=this.$el.querySelector(`.${te}`);e&&(await this.$nextTick(),e.focus({preventScroll:!0}))}}},se=ne,ie=(0,w.Z)(se,H,U,!1,null,"6adda760",null),re=ie.exports,oe={name:"SecondaryDropdown",components:{DropdownCustom:re},props:{options:{type:Array,required:!0},currentOption:{type:String,required:!0},sectionTracker:{type:String,required:!1}},methods:{ariaCurrent(e){return this.currentOption===e&&"section"},setActive(e,t,n,s){t(s),this.$emit("select-section",e.path),n()}}},ae=oe,le=(0,w.Z)(ae,j,E,!1,null,"618ff780",null),ce=le.exports,ue=function(){var e=this,t=e._self._c;return t("DropdownCustom",{staticClass:"tutorial-dropdown",attrs:{value:e.currentOption,"aria-label":e.$t("tutorials.nav.current",{thing:e.$tc("tutorials.title",1)}),isSmall:""},scopedSlots:e._u([{key:"default",fn:function({closeAndFocusToggler:n,contentClasses:s,closeDropdown:i,navigateOverOptions:r,OptionClass:o,ActiveOptionClass:a}){return[t("ul",{staticClass:"options",class:s,attrs:{tabindex:"0"}},e._l(e.options,(function(s){return t("ReferenceUrlProvider",{key:s.reference,attrs:{reference:s.reference},scopedSlots:e._u([{key:"default",fn:function({title:l}){return[t("li",{staticClass:"chapter-list",attrs:{role:"group"}},[t("p",{staticClass:"chapter-name"},[e._v(e._s(l))]),t("ul",{attrs:{role:"listbox"}},e._l(s.projects,(function(s){return t("ReferenceUrlProvider",{key:s.reference,attrs:{reference:s.reference},scopedSlots:e._u([{key:"default",fn:function({urlWithParams:s,title:l}){return[t("router-link",{attrs:{to:s,custom:""},scopedSlots:e._u([{key:"default",fn:function({navigate:s,isActive:c}){return[t("li",{class:{[o]:!0,[a]:c},attrs:{value:l,"aria-selected":c,"aria-current":!!c&&"tutorial",tabindex:-1},on:{click:function(t){return e.setActive(s,i,t)},keydown:[function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"enter",13,t.key,"Enter")?null:e.setActive(s,i,t)},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"esc",27,t.key,["Esc","Escape"])?null:n.apply(null,arguments)},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"tab",9,t.key,"Tab")?null:n.apply(null,arguments)},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"down",40,t.key,["Down","ArrowDown"])?null:(t.preventDefault(),r(t,1))},function(t){return!t.type.indexOf("key")&&e._k(t.keyCode,"up",38,t.key,["Up","ArrowUp"])?null:(t.preventDefault(),r(t,-1))}]}},[e._v(" "+e._s(l)+" ")])]}}],null,!0)})]}}],null,!0)})})),1)])]}}],null,!0)})})),1)]}}])})},de=[],pe={name:"PrimaryDropdown",components:{DropdownCustom:re,ReferenceUrlProvider:N},props:{options:{type:Array,required:!0},currentOption:{type:String,required:!0}},methods:{setActive(e,t,n){e(n),t()}}},he=pe,me=(0,w.Z)(he,ue,de,!1,null,"03cbd7f7",null),ve=me.exports;const fe={title:"Introduction",url:"#introduction",reference:"introduction",sectionNumber:0,depth:0};var ge={name:"NavigationBar",components:{NavTitleContainer:D.Z,NavBase:P.Z,ReferenceUrlProvider:N,PrimaryDropdown:ve,SecondaryDropdown:ce,MobileDropdown:V,ChevronIcon:S},mixins:[q.Z,A.Z],props:{chapters:{type:Array,required:!0},technology:{type:String,required:!0},topic:{type:String,required:!0},rootReference:{type:String,required:!0},identifierUrl:{type:String,required:!0}},data(){return{currentSection:fe,tutorialState:this.store.state}},watch:{pageSectionWithHighestVisibility(e){e&&(this.currentSection=e)}},computed:{currentProject(){return this.chapters.reduce(((e,{projects:t})=>e.concat(t)),[]).find((e=>e.reference===this.identifierUrl))},pageSections(){if(!this.currentProject)return[];const e=[fe].concat(this.currentProject.sections);return this.tutorialState.linkableSections.map(((t,n)=>{const s=e[n],i=this.references[s.reference],{url:r,title:o}=i||s;return{...t,title:o,path:r}}))},optionsForSections(){return this.pageSections.map((({depth:e,path:t,title:n})=>({depth:e,path:t,title:n})))},pageSectionWithHighestVisibility(){return[...this.pageSections].sort(((e,t)=>t.visibility-e.visibility)).find((e=>e.visibility>0))},sectionIndicatorText(){const e=this.tutorialState.linkableSections.length-1,{sectionNumber:t}=this.currentSection||{};if(0!==t)return this.$t("tutorials.section-of",{number:t,total:e})}},methods:{onSelectSection(e){const t=e.split("#")[1];this.handleFocusAndScroll(t)}}},ye=ge,Ce=(0,w.Z)(ye,v,f,!1,null,"1d3fe8ed",null),be=Ce.exports,_e=n(2974),we=function(){var e=this,t=e._self._c;return t("div",{staticClass:"body"},[t("BodyContent",{attrs:{content:e.content}})],1)},ke=[],Se=function(){var e=this,t=e._self._c;return t("article",{staticClass:"body-content"},e._l(e.content,(function(n,s){return t(e.componentFor(n),e._b({key:s,tag:"component",staticClass:"layout"},"component",e.propsFor(n),!1))})),1)},xe=[],Ae=function(){var e=this,t=e._self._c;return t("div",{staticClass:"columns",class:e.classes},[e._l(e.columns,(function(n,s){return[t("Asset",{key:n.media,attrs:{identifier:n.media,videoAutoplays:!1}}),n.content?t("ContentNode",{key:s,attrs:{content:n.content}}):e._e()]}))],2)},Te=[],Ie=n(4655),$e=function(){var e=this,t=e._self._c;return t("BaseContentNode",{attrs:{content:e.articleContent}})},Ne=[],qe=n(9519),Pe={name:"ContentNode",components:{BaseContentNode:qe["default"]},props:qe["default"].props,computed:{articleContent(){return this.map((e=>{switch(e.type){case qe["default"].BlockType.codeListing:return{...e,showLineNumbers:!0};case qe["default"].BlockType.heading:{const{anchor:t,...n}=e;return n}default:return e}}))}},methods:qe["default"].methods,BlockType:qe["default"].BlockType,InlineType:qe["default"].InlineType},De=Pe,Ze=(0,w.Z)(De,$e,Ne,!1,null,"0861b5be",null),Re=Ze.exports,Oe={name:"Columns",components:{Asset:Ie.Z,ContentNode:Re},props:{columns:{type:Array,required:!0}},computed:{classes(){return{"cols-2":2===this.columns.length,"cols-3":3===this.columns.length}}}},Le=Oe,Be=(0,w.Z)(Le,Ae,Te,!1,null,"30edf911",null),Me=Be.exports,Fe=function(){var e=this,t=e._self._c;return t("div",{staticClass:"content-and-media",class:e.classes},[t("ContentNode",{attrs:{content:e.content}}),t("Asset",{attrs:{identifier:e.media}})],1)},Ve=[];const je={leading:"leading",trailing:"trailing"};var Ee={name:"ContentAndMedia",components:{Asset:Ie.Z,ContentNode:Re},props:{content:Re.props.content,media:Ie.Z.props.identifier,mediaPosition:{type:String,default:()=>je.trailing,validator:e=>Object.prototype.hasOwnProperty.call(je,e)}},computed:{classes(){return{"media-leading":this.mediaPosition===je.leading,"media-trailing":this.mediaPosition===je.trailing}}},MediaPosition:je},He=Ee,Ue=(0,w.Z)(He,Fe,Ve,!1,null,"3fa44f9e",null),ze=Ue.exports,Ge=function(){var e=this,t=e._self._c;return t("div",{staticClass:"full-width"},e._l(e.groups,(function(n,s){return t(e.componentFor(n),e._b({key:s,tag:"component",staticClass:"group"},"component",e.propsFor(n),!1),[t("ContentNode",{attrs:{content:n.content}})],1)})),1)},We=[],Qe=function(){var e=this,t=e._self._c;return t(e.tag,{tag:"component",attrs:{id:e.anchor}},[e._t("default")],2)},Ke=[],Xe=n(9146),Ye={name:"LinkableElement",mixins:[Xe["default"]],inject:{navigationBarHeight:{default(){}},store:{default(){return{addLinkableSection(){},updateLinkableSection(){}}}}},props:{anchor:{type:String,required:!0},depth:{type:Number,default:()=>0},tag:{type:String,default:()=>"div"},title:{type:String,required:!0}},computed:{intersectionRootMargin(){const e=this.navigationBarHeight?`-${this.navigationBarHeight}px`:"0%";return`${e} 0% -50% 0%`}},created(){this.store.addLinkableSection({anchor:this.anchor,depth:this.depth,title:this.title,visibility:0})},methods:{onIntersect(e){const t=Math.min(1,e.intersectionRatio);this.store.updateLinkableSection({anchor:this.anchor,depth:this.depth,title:this.title,visibility:t})}}},Je=Ye,et=(0,w.Z)(Je,Qe,Ke,!1,null,null,null),tt=et.exports;const{BlockType:nt}=Re;var st={name:"FullWidth",components:{ContentNode:Re,LinkableElement:tt},props:Re.props,computed:{groups:({content:e})=>e.reduce(((e,t)=>0===e.length||t.type===nt.heading?[...e,{heading:t.type===nt.heading?t:null,content:[t]}]:[...e.slice(0,e.length-1),{heading:e[e.length-1].heading,content:e[e.length-1].content.concat(t)}]),[])},methods:{componentFor(e){return e.heading?tt:"div"},depthFor(e){switch(e.level){case 1:case 2:return 0;default:return 1}},propsFor(e){return e.heading?{anchor:e.heading.anchor,depth:this.depthFor(e.heading),title:e.heading.text}:{}}}},it=st,rt=(0,w.Z)(it,Ge,We,!1,null,"5b4a8b3c",null),ot=rt.exports;const at={columns:"columns",contentAndMedia:"contentAndMedia",fullWidth:"fullWidth"};var lt={name:"BodyContent",props:{content:{type:Array,required:!0,validator:e=>e.every((({kind:e})=>Object.prototype.hasOwnProperty.call(at,e)))}},methods:{componentFor(e){return{[at.columns]:Me,[at.contentAndMedia]:ze,[at.fullWidth]:ot}[e.kind]},propsFor(e){const{content:t,kind:n,media:s,mediaPosition:i}=e;return{[at.columns]:{columns:t},[at.contentAndMedia]:{content:t,media:s,mediaPosition:i},[at.fullWidth]:{content:t}}[n]}},LayoutKind:at},ct=lt,ut=(0,w.Z)(ct,Se,xe,!1,null,"4d5a806e",null),dt=ut.exports,pt={name:"Body",components:{BodyContent:dt},props:dt.props},ht=pt,mt=(0,w.Z)(ht,we,ke,!1,null,"20dca692",null),vt=mt.exports,ft=function(){var e=this,t=e._self._c;return t("TutorialCTA",e._b({},"TutorialCTA",e.$props,!1))},gt=[],yt=function(){var e=this,t=e._self._c;return t("BaseCTA",e._b({attrs:{label:e.$t("tutorials.next")}},"BaseCTA",e.baseProps,!1))},Ct=[],bt=function(){var e=this,t=e._self._c;return t("div",{staticClass:"call-to-action"},[t("Row",[t("LeftColumn",[t("span",{staticClass:"label"},[e._v(e._s(e.label))]),t("h2",[e._v(" "+e._s(e.title)+" ")]),e.abstract?t("ContentNode",{staticClass:"description",attrs:{content:[e.abstractParagraph]}}):e._e(),e.action?t("Button",{attrs:{action:e.action}}):e._e()],1),t("RightColumn",{staticClass:"right-column"},[e.media?t("Asset",{staticClass:"media",attrs:{identifier:e.media}}):e._e()],1)],1)],1)},_t=[],wt=n(9649),kt=n(1576),St=n(7605),xt={name:"CallToAction",components:{Asset:Ie.Z,Button:St.Z,ContentNode:qe["default"],LeftColumn:{render(e){return e(kt.Z,{props:{span:{large:5,small:12}}},this.$slots.default)}},RightColumn:{render(e){return e(kt.Z,{props:{span:{large:6,small:12}}},this.$slots.default)}},Row:wt.Z},props:{title:{type:String,required:!0},label:{type:String,required:!0},abstract:{type:Array,required:!1},action:{type:Object,required:!1},media:{type:String,required:!1}},computed:{abstractParagraph(){return{type:"paragraph",inlineContent:this.abstract}}}},At=xt,Tt=(0,w.Z)(At,bt,_t,!1,null,"2bfdf182",null),It=Tt.exports,$t={name:"CallToAction",components:{BaseCTA:It},computed:{baseProps(){return{title:this.title,abstract:this.abstract,action:this.action,media:this.media}}},props:{title:{type:String,required:!0},abstract:{type:Array,required:!1},action:{type:Object,required:!1},media:{type:String,required:!1}}},Nt=$t,qt=(0,w.Z)(Nt,yt,Ct,!1,null,null,null),Pt=qt.exports,Dt={name:"CallToAction",components:{TutorialCTA:Pt},props:Pt.props},Zt=Dt,Rt=(0,w.Z)(Zt,ft,gt,!1,null,"426a965c",null),Ot=Rt.exports,Lt=function(){var e=this,t=e._self._c;return t("TutorialHero",e._b({},"TutorialHero",e.$props,!1))},Bt=[],Mt=function(){var e=this,t=e._self._c;return t("LinkableSection",{staticClass:"tutorial-hero",attrs:{anchor:"introduction",title:e.sectionTitle}},[t("div",{staticClass:"hero dark"},[e.backgroundImageUrl?t("div",{staticClass:"bg",style:e.bgStyle}):e._e(),e._t("above-title"),t("Row",[t("Column",[t("Headline",{attrs:{level:1},scopedSlots:e._u([e.chapter?{key:"eyebrow",fn:function(){return[e._v(e._s(e.chapter))]},proxy:!0}:null],null,!0)},[e._v(" "+e._s(e.title)+" ")]),e.content||e.video?t("div",{staticClass:"intro"},[e.content?t("ContentNode",{attrs:{content:e.content}}):e._e(),e.video?[t("p",[t("a",{staticClass:"call-to-action",attrs:{href:"#"},on:{click:function(t){return t.preventDefault(),e.toggleCallToActionModal.apply(null,arguments)}}},[e._v(" Watch intro video "),t("PlayIcon",{staticClass:"cta-icon icon-inline"})],1)]),t("GenericModal",{attrs:{visible:e.callToActionModalVisible,isFullscreen:"",theme:"dark"},on:{"update:visible":function(t){e.callToActionModalVisible=t}}},[t("Asset",{directives:[{name:"show",rawName:"v-show",value:e.callToActionModalVisible,expression:"callToActionModalVisible"}],ref:"asset",staticClass:"video-asset",attrs:{identifier:e.video},on:{videoEnded:e.handleVideoEnd}})],1)]:e._e()],2):e._e(),t("Metadata",{staticClass:"metadata",attrs:{projectFilesUrl:e.projectFilesUrl,estimatedTimeInMinutes:e.estimatedTimeInMinutes,xcodeRequirement:e.xcodeRequirementData}})],1)],1)],2)])},Ft=[],Vt=function(){var e=this,t=e._self._c;return t("div",{staticClass:"headline"},[e.$slots.eyebrow?t("span",{staticClass:"eyebrow"},[e._t("eyebrow")],2):e._e(),t("Heading",{staticClass:"heading",attrs:{level:e.level}},[e._t("default")],2)],1)},jt=[];const Et=1,Ht=6,Ut={type:Number,required:!0,validator:e=>e>=Et&&e<=Ht},zt={name:"Heading",render:function(e){return e(`h${this.level}`,this.$slots.default)},props:{level:Ut}};var Gt={name:"Headline",components:{Heading:zt},props:{level:Ut}},Wt=Gt,Qt=(0,w.Z)(Wt,Vt,jt,!1,null,"d46a1474",null),Kt=Qt.exports,Xt=n(5590),Yt=n(6698),Jt=n(5947),en=function(){var e=this,t=e._self._c;return t("div",{staticClass:"metadata"},[e.estimatedTimeInMinutes?t("div",{staticClass:"item",attrs:{"aria-label":`\n ${e.$tc("tutorials.time.minutes.full",e.estimatedTimeInMinutes,{count:e.estimatedTimeInMinutes})}\n ${e.$t("tutorials.estimated-time")}\n `}},[t("div",{staticClass:"content",attrs:{"aria-hidden":"true"}},[t("i18n",{staticClass:"duration",attrs:{path:"tutorials.time.format",tag:"div"},scopedSlots:e._u([{key:"number",fn:function(){return[e._v(" "+e._s(e.estimatedTimeInMinutes)+" ")]},proxy:!0},{key:"minutes",fn:function(){return[t("div",{staticClass:"minutes"},[e._v(e._s(e.$tc("tutorials.time.minutes.short",e.estimatedTimeInMinutes))+" ")])]},proxy:!0}],null,!1,3313752798)})],1),t("div",{staticClass:"bottom",attrs:{"aria-hidden":"true"}},[e._v(e._s(e.$t("tutorials.estimated-time")))])]):e._e(),e.projectFilesUrl?t("div",{staticClass:"item"},[t("DownloadIcon",{staticClass:"item-large-icon icon-inline"}),t("div",{staticClass:"content bottom"},[t("a",{staticClass:"content-link project-download",attrs:{href:e.projectFilesUrl}},[e._v(" "+e._s(e.$t("tutorials.project-files"))+" "),t("InlineDownloadIcon",{staticClass:"small-icon icon-inline"})],1)])],1):e._e(),e.xcodeRequirement?t("div",{staticClass:"item"},[t("XcodeIcon",{staticClass:"item-large-icon icon-inline"}),t("div",{staticClass:"content bottom"},[e.isTargetIDE?t("span",[e._v(e._s(e.xcodeRequirement.title))]):t("a",{staticClass:"content-link",attrs:{href:e.xcodeRequirement.url}},[e._v(" "+e._s(e.xcodeRequirement.title)+" "),t("InlineChevronRightIcon",{staticClass:"icon-inline small-icon xcode-icon"})],1)])],1):e._e()])},tn=[],nn=n(7214),sn=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"xcode-icon",attrs:{viewBox:"0 0 14 14",themeId:"xcode"}},[t("path",{attrs:{d:"M2.668 4.452l-1.338-2.229 0.891-0.891 2.229 1.338 1.338 2.228 3.667 3.666 0.194-0.194 2.933 2.933c0.13 0.155 0.209 0.356 0.209 0.576 0 0.497-0.403 0.9-0.9 0.9-0.22 0-0.421-0.079-0.577-0.209l0.001 0.001-2.934-2.933 0.181-0.181-3.666-3.666z"}}),t("path",{attrs:{d:"M11.824 1.277l-0.908 0.908c-0.091 0.091-0.147 0.216-0.147 0.354 0 0.106 0.033 0.205 0.090 0.286l-0.001-0.002 0.058 0.069 0.185 0.185c0.090 0.090 0.215 0.146 0.353 0.146 0.107 0 0.205-0.033 0.286-0.090l-0.002 0.001 0.069-0.057 0.909-0.908c0.118 0.24 0.187 0.522 0.187 0.82 0 1.045-0.848 1.893-1.893 1.893-0.296 0-0.577-0.068-0.826-0.189l0.011 0.005-5.5 5.5c0.116 0.238 0.184 0.518 0.184 0.813 0 1.045-0.848 1.893-1.893 1.893-0.296 0-0.576-0.068-0.826-0.189l0.011 0.005 0.908-0.909c0.090-0.090 0.146-0.215 0.146-0.353 0-0.107-0.033-0.205-0.090-0.286l0.001 0.002-0.057-0.069-0.185-0.185c-0.091-0.091-0.216-0.147-0.354-0.147-0.106 0-0.205 0.033-0.286 0.090l0.002-0.001-0.069 0.058-0.908 0.908c-0.116-0.238-0.184-0.518-0.184-0.813 0-1.045 0.847-1.892 1.892-1.892 0.293 0 0.571 0.067 0.819 0.186l-0.011-0.005 5.5-5.5c-0.116-0.238-0.184-0.519-0.184-0.815 0-1.045 0.847-1.892 1.892-1.892 0.296 0 0.577 0.068 0.827 0.19l-0.011-0.005z"}})])},rn=[],on={name:"XcodeIcon",components:{SVGIcon:C.Z}},an=on,ln=(0,w.Z)(an,sn,rn,!1,null,null,null),cn=ln.exports,un=n(8785),dn=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"inline-download-icon",attrs:{viewBox:"0 0 14 14",themeId:"inline-download"}},[t("path",{attrs:{d:"M7 0.5c3.59 0 6.5 2.91 6.5 6.5s-2.91 6.5-6.5 6.5c-3.59 0-6.5-2.91-6.5-6.5s2.91-6.5 6.5-6.5zM7 1.5c-3.038 0-5.5 2.462-5.5 5.5s2.462 5.5 5.5 5.5c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5z"}}),t("path",{attrs:{d:"M7.51 2.964l-0.001 5.431 1.308-2.041 0.842 0.539-2.664 4.162-2.633-4.164 0.845-0.534 1.303 2.059 0.001-5.452z"}})])},pn=[],hn={name:"InlineDownloadIcon",components:{SVGIcon:C.Z}},mn=hn,vn=(0,w.Z)(mn,dn,pn,!1,null,null,null),fn=vn.exports,gn={name:"HeroMetadata",components:{InlineDownloadIcon:fn,InlineChevronRightIcon:un.Z,DownloadIcon:nn.Z,XcodeIcon:cn},inject:["isTargetIDE"],props:{projectFilesUrl:{type:String},estimatedTimeInMinutes:{type:Number},xcodeRequirement:{type:Object,required:!1}}},yn=gn,Cn=(0,w.Z)(yn,en,tn,!1,null,"94ff76c0",null),bn=Cn.exports,_n={name:"Hero",components:{PlayIcon:Yt.Z,GenericModal:Xt.Z,Column:{render(e){return e(kt.Z,{props:{span:{large:7,medium:9,small:12}}},this.$slots.default)}},ContentNode:qe["default"],Headline:Kt,Metadata:bn,Row:wt.Z,Asset:Ie.Z,LinkableSection:tt},mixins:[A.Z],props:{title:{type:String,required:!0},chapter:{type:String},content:{type:Array},projectFiles:{type:String},estimatedTimeInMinutes:{type:Number},xcodeRequirement:{type:String,required:!1},video:{type:String},backgroundImage:{type:String}},computed:{backgroundImageUrl(){const e=this.references[this.backgroundImage]||{},{variants:t=[]}=e,n=t.find((e=>e.traits.includes("light")));return(0,Jt.AH)((n||{}).url)},projectFilesUrl(){return this.projectFiles?(0,Jt.AH)(this.references[this.projectFiles].url):null},bgStyle(){return{backgroundImage:(0,Jt.eZ)(this.backgroundImageUrl)}},xcodeRequirementData(){return this.references[this.xcodeRequirement]},sectionTitle(){return"Introduction"}},data(){return{callToActionModalVisible:!1}},methods:{async toggleCallToActionModal(){this.callToActionModalVisible=!0,await this.$nextTick();const e=this.$refs.asset.$el.querySelector("video");if(e)try{await e.play(),e.muted=!1}catch(t){}},handleVideoEnd(){this.callToActionModalVisible=!1}}},wn=_n,kn=(0,w.Z)(wn,Mt,Ft,!1,null,"2a434750",null),Sn=kn.exports,xn={name:"Hero",components:{TutorialHero:Sn},props:Sn.props},An=xn,Tn=(0,w.Z)(An,Lt,Bt,!1,null,"35a9482f",null),In=Tn.exports,$n=function(){var e=this,t=e._self._c;return t("TutorialAssessments",e._b({scopedSlots:e._u([{key:"success",fn:function(){return[t("p",[e._v("Great job, you've answered all the questions for this article.")])]},proxy:!0}])},"TutorialAssessments",e.$props,!1))},Nn=[],qn=function(){var e=this,t=e._self._c;return t("LinkableSection",{staticClass:"assessments-wrapper",attrs:{anchor:e.anchor,title:e.title}},[t("Row",{ref:"assessments",staticClass:"assessments"},[t("MainColumn",[t("Row",{staticClass:"banner"},[t("HeaderColumn",[t("h2",{staticClass:"title"},[e._v(e._s(e.title))])])],1),e.completed?t("div",{staticClass:"success"},[e._t("success",(function(){return[t("p",[e._v(e._s(e.SuccessMessage))])]}))],2):t("div",[t("Progress",e._b({ref:"progress"},"Progress",e.progress,!1)),t("Quiz",{key:e.activeIndex,attrs:{choices:e.activeAssessment.choices,content:e.activeAssessment.content,isLast:e.isLast,title:e.activeAssessment.title},on:{submit:e.onSubmit,advance:e.onAdvance,"see-results":e.onSeeResults}})],1),t("div",{staticClass:"visuallyhidden",attrs:{"aria-live":"assertive"}},[e.completed?e._t("success",(function(){return[e._v(" "+e._s(e.SuccessMessage)+" ")]})):e._e()],2)],1)],1)],1)},Pn=[],Dn=function(){var e=this,t=e._self._c;return t("Row",[t("p",{staticClass:"title"},[e._v(e._s(e.$t("tutorials.question-of",{index:e.index,total:e.total})))])])},Zn=[],Rn={name:"AssessmentsProgress",components:{Row:wt.Z},props:{index:{type:Number,required:!0},total:{type:Number,required:!0}}},On=Rn,Ln=(0,w.Z)(On,Dn,Zn,!1,null,"28135d78",null),Bn=Ln.exports,Mn=function(){var e=this,t=e._self._c;return t("div",{staticClass:"quiz"},[t("ContentNode",{staticClass:"title",attrs:{content:e.title}}),e.content?t("ContentNode",{staticClass:"question-content",attrs:{content:e.content}}):e._e(),t("fieldset",{staticClass:"choices"},[t("legend",{staticClass:"visuallyhidden"},[e._v(e._s(e.$t("tutorials.assessment.legend")))]),e._l(e.choices,(function(n,s){return t("label",{key:s,class:e.choiceClasses[s]},[t(e.getIconComponent(s),{tag:"component",staticClass:"choice-icon"}),t("input",{directives:[{name:"model",rawName:"v-model",value:e.selectedIndex,expression:"selectedIndex"}],attrs:{type:"radio",name:"assessment"},domProps:{value:s,checked:e._q(e.selectedIndex,s)},on:{change:function(t){e.selectedIndex=s}}}),t("ContentNode",{staticClass:"question",attrs:{content:n.content}}),e.userChoices[s].checked?[t("ContentNode",{staticClass:"answer",attrs:{content:n.justification}}),n.reaction?t("p",{staticClass:"answer"},[e._v(e._s(n.reaction))]):e._e()]:e._e()],2)}))],2),t("div",{staticClass:"visuallyhidden",attrs:{"aria-live":"assertive"}},[null!=e.checkedIndex?t("i18n",{attrs:{path:"tutorials.assessment.answer-result",tag:"span"},scopedSlots:e._u([{key:"answer",fn:function(){return[t("ContentNode",{staticClass:"question",attrs:{content:e.choices[e.checkedIndex].content}})]},proxy:!0},{key:"result",fn:function(){return[e._v(e._s(e.choices[e.checkedIndex].isCorrect?e.$t("tutorials.assessment.correct"):e.$t("tutorials.assessment.incorrect")))]},proxy:!0}],null,!1,511264553)}):e._e()],1),t("div",{staticClass:"controls"},[t("ButtonLink",{staticClass:"check",attrs:{disabled:null===e.selectedIndex||e.showNextQuestion},nativeOn:{click:function(t){return e.submit.apply(null,arguments)}}},[e._v(" "+e._s(e.$t("tutorials.submit"))+" ")]),e.isLast?t("ButtonLink",{staticClass:"results",attrs:{disabled:!e.showNextQuestion},nativeOn:{click:function(t){return e.seeResults.apply(null,arguments)}}},[e._v(" "+e._s(e.$t("tutorials.next"))+" ")]):t("ButtonLink",{staticClass:"next",attrs:{disabled:!e.showNextQuestion},nativeOn:{click:function(t){return e.advance.apply(null,arguments)}}},[e._v(" "+e._s(e.$t("tutorials.assessment.next-question"))+" ")])],1)],1)},Fn=[],Vn=n(5281),jn=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"reset-circle-icon",attrs:{viewBox:"0 0 14 14",themeId:"reset-circle"}},[t("path",{attrs:{d:"M7 0.5c3.59 0 6.5 2.91 6.5 6.5s-2.91 6.5-6.5 6.5c-3.59 0-6.5-2.91-6.5-6.5v0c0-3.59 2.91-6.5 6.5-6.5v0zM7 1.5c-3.038 0-5.5 2.462-5.5 5.5s2.462 5.5 5.5 5.5c3.038 0 5.5-2.462 5.5-5.5v0c0-3.038-2.462-5.5-5.5-5.5v0z"}}),t("path",{attrs:{d:"M3.828 4.539l0.707-0.707 5.657 5.657-0.707 0.707-5.657-5.657z"}}),t("path",{attrs:{d:"M3.828 9.489l5.657-5.657 0.707 0.707-5.657 5.657-0.707-0.707z"}})])},En=[],Hn={name:"ResetCircleIcon",components:{SVGIcon:C.Z}},Un=Hn,zn=(0,w.Z)(Un,jn,En,!1,null,null,null),Gn=zn.exports,Wn=function(){var e=this,t=e._self._c;return t("SVGIcon",{staticClass:"check-circle-icon",attrs:{viewBox:"0 0 14 14",themeId:"check-circle"}},[t("path",{attrs:{d:"M7 0.5c3.59 0 6.5 2.91 6.5 6.5s-2.91 6.5-6.5 6.5c-3.59 0-6.5-2.91-6.5-6.5v0c0-3.59 2.91-6.5 6.5-6.5v0zM7 1.5c-3.038 0-5.5 2.462-5.5 5.5s2.462 5.5 5.5 5.5c3.038 0 5.5-2.462 5.5-5.5v0c0-3.038-2.462-5.5-5.5-5.5v0z"}}),t("path",{attrs:{d:"M9.626 3.719l0.866 0.5-3.5 6.062-3.464-2 0.5-0.866 2.6 1.5z"}})])},Qn=[],Kn={name:"CheckCircleIcon",components:{SVGIcon:C.Z}},Xn=Kn,Yn=(0,w.Z)(Xn,Wn,Qn,!1,null,null,null),Jn=Yn.exports,es={name:"Quiz",components:{CheckCircleIcon:Jn,ResetCircleIcon:Gn,ContentNode:qe["default"],ButtonLink:Vn.Z},props:{content:{type:Array,required:!1},choices:{type:Array,required:!0},isLast:{type:Boolean,default:!1},title:{type:Array,required:!0}},data(){return{userChoices:this.choices.map((()=>({checked:!1}))),selectedIndex:null,checkedIndex:null}},computed:{correctChoices(){return this.choices.reduce(((e,t,n)=>t.isCorrect?e.add(n):e),new Set)},choiceClasses(){return this.userChoices.map(((e,t)=>({choice:!0,active:this.selectedIndex===t,disabled:e.checked||this.showNextQuestion,correct:e.checked&&this.choices[t].isCorrect,incorrect:e.checked&&!this.choices[t].isCorrect})))},showNextQuestion(){return Array.from(this.correctChoices).every((e=>this.userChoices[e].checked))}},methods:{getIconComponent(e){const t=this.userChoices[e];if(t&&t.checked)return this.choices[e].isCorrect?Jn:Gn},submit(){this.$set(this.userChoices,this.selectedIndex,{checked:!0}),this.checkedIndex=this.selectedIndex,this.$emit("submit")},advance(){this.$emit("advance")},seeResults(){this.$emit("see-results")}}},ts=es,ns=(0,w.Z)(ts,Mn,Fn,!1,null,"61b03ec2",null),ss=ns.exports;const is=12,rs="tutorials.assessment.success-message";var os={name:"Assessments",constants:{SuccessMessage:rs},components:{LinkableSection:tt,Quiz:ss,Progress:Bn,Row:wt.Z,HeaderColumn:{render(e){return e(kt.Z,{props:{isCentered:{large:!0},span:{large:10}}},this.$slots.default)}},MainColumn:{render(e){return e(kt.Z,{props:{isCentered:{large:!0},span:{large:10,medium:10,small:12}}},this.$slots.default)}}},props:{assessments:{type:Array,required:!0},anchor:{type:String,required:!0}},inject:["navigationBarHeight"],data(){return{activeIndex:0,completed:!1,SuccessMessage:this.$t(rs)}},computed:{activeAssessment(){return this.assessments[this.activeIndex]},isLast(){return this.activeIndex===this.assessments.length-1},progress(){return{index:this.activeIndex+1,total:this.assessments.length}},title(){return this.$t("tutorials.assessment.check-your-understanding")}},methods:{scrollTo(e,t=0){e.scrollIntoView(!0),window.scrollBy(0,-this.navigationBarHeight-t)},onSubmit(){this.$nextTick((()=>{this.scrollTo(this.$refs.progress.$el,is)}))},onAdvance(){this.activeIndex+=1,this.$nextTick((()=>{this.scrollTo(this.$refs.progress.$el,is)}))},onSeeResults(){this.completed=!0,this.$nextTick((()=>{this.scrollTo(this.$refs.assessments.$el,is)}))}}},as=os,ls=(0,w.Z)(as,qn,Pn,!1,null,"65e3c02c",null),cs=ls.exports,us={name:"Assessments",components:{TutorialAssessments:cs},props:cs.props},ds=us,ps=(0,w.Z)(ds,$n,Nn,!1,null,"6db06128",null),hs=ps.exports;const ms={articleBody:"articleBody",callToAction:"callToAction",hero:"hero",assessments:"assessments"};var vs={name:"Article",components:{NavigationBar:be,PortalTarget:h.YC},mixins:[_e.Z],inject:{isTargetIDE:{default:!1},store:{default(){return{reset(){},setReferences(){}}}}},props:{hierarchy:{type:Object,required:!0},metadata:{type:Object,required:!0},references:{type:Object,required:!0},sections:{type:Array,required:!0,validator:e=>e.every((({kind:e})=>Object.prototype.hasOwnProperty.call(ms,e)))},identifierUrl:{type:String,required:!0}},computed:{heroSection(){return this.sections.find(this.isHero)},heroTitle(){return(this.heroSection||{}).title},pageTitle(){return this.heroTitle?`${this.heroTitle} — ${this.metadata.category} Tutorials`:void 0},pageDescription:({heroSection:e,extractFirstParagraphText:t})=>e?t(e.content):null},methods:{componentFor(e){const{kind:t}=e;return{[ms.articleBody]:vt,[ms.callToAction]:Ot,[ms.hero]:In,[ms.assessments]:hs}[t]},isHero(e){return e.kind===ms.hero},propsFor(e){const{abstract:t,action:n,anchor:s,assessments:i,backgroundImage:r,chapter:o,content:a,estimatedTimeInMinutes:l,kind:c,media:u,projectFiles:d,title:p,video:h,xcodeRequirement:m}=e;return{[ms.articleBody]:{content:a},[ms.callToAction]:{abstract:t,action:n,media:u,title:p},[ms.hero]:{backgroundImage:r,chapter:o,content:a,estimatedTimeInMinutes:l,projectFiles:d,title:p,video:h,xcodeRequirement:m},[ms.assessments]:{anchor:s,assessments:i}}[c]}},created(){m["default"].setAvailableLocales(this.metadata.availableLocales),this.store.reset(),this.store.setReferences(this.references)},watch:{references(e){this.store.setReferences(e)},"metadata.availableLocales":function(e){m["default"].setAvailableLocales(e)}},SectionKind:ms},fs=vs,gs=(0,w.Z)(fs,d,p,!1,null,"1b2e3b6a",null),ys=gs.exports,Cs=function(){var e=this,t=e._self._c;return t("div",{staticClass:"tutorial"},[e.isTargetIDE?e._e():t("NavigationBar",{attrs:{technology:e.metadata.category,chapters:e.hierarchy.modules,topic:e.tutorialTitle||"",rootReference:e.hierarchy.reference,identifierUrl:e.identifierUrl}}),t("main",{attrs:{id:"main",tabindex:"0"}},[e._l(e.sections,(function(e,n){return t("Section",{key:n,attrs:{section:e}})})),t("BreakpointEmitter",{on:{change:e.handleBreakpointChange}})],2),t("PortalTarget",{attrs:{name:"modal-destination",multiple:""}})],1)},bs=[],_s=n(8571),ws=function(){var e=this,t=e._self._c;return t("div",{staticClass:"sections"},e._l(e.tasks,(function(n,s){return t("Section",e._b({key:s,attrs:{id:n.anchor,sectionNumber:s+1,isRuntimePreviewVisible:e.isRuntimePreviewVisible},on:{"runtime-preview-toggle":e.onRuntimePreviewToggle}},"Section",n,!1))})),1)},ks=[],Ss=function(){var e=this,t=e._self._c;return t("LinkableSection",{staticClass:"section",attrs:{anchor:e.anchor,title:e.introProps.title}},[t("Intro",e._b({},"Intro",e.introProps,!1)),e.stepsSection.length>0?t("Steps",{attrs:{content:e.stepsSection,isRuntimePreviewVisible:e.isRuntimePreviewVisible,sectionNumber:e.sectionNumber},on:{"runtime-preview-toggle":e.onRuntimePreviewToggle}}):e._e()],1)},xs=[],As=function(){var e=this,t=e._self._c;return t("div",{staticClass:"intro-container"},[t("Row",{class:["intro",`intro-${e.sectionNumber}`,{ide:e.isTargetIDE}]},[t("Column",{staticClass:"left"},[t("Headline",{attrs:{level:2},scopedSlots:e._u([{key:"eyebrow",fn:function(){return[t("router-link",{attrs:{to:e.sectionLink}},[e._v(" "+e._s(e.$t("sections.title",{number:e.sectionNumber}))+" ")])]},proxy:!0}])},[e._v(" "+e._s(e.title)+" ")]),t("ContentNode",{attrs:{content:e.content}})],1),t("Column",{staticClass:"right"},[t("div",{staticClass:"media"},[e.media?t("Asset",{attrs:{videoAutoplays:"",videoMuted:"",identifier:e.media}}):e._e()],1)])],1),e.expandedSections.length>0?t("ExpandedIntro",{staticClass:"expanded-intro",attrs:{content:e.expandedSections}}):e._e()],1)},Ts=[],Is={name:"SectionIntro",inject:{isTargetIDE:{default:()=>!1}},components:{Asset:Ie.Z,ContentNode:qe["default"],ExpandedIntro:dt,Headline:Kt,Row:wt.Z,Column:{render(e){return e(kt.Z,{props:{span:{large:6,small:12}}},this.$slots.default)}}},props:{sectionAnchor:{type:String,required:!0},content:{type:Array,required:!0},media:{type:String,required:!0},title:{type:String,required:!0},sectionNumber:{type:Number,required:!0},expandedSections:{type:Array,default:()=>[]}},methods:{focus(){this.$emit("focus",this.media)}},computed:{sectionLink(){return{path:this.$route.path,hash:this.sectionAnchor,query:this.$route.query}}}},$s=Is,Ns=(0,w.Z)($s,As,Ts,!1,null,"7dcf2d10",null),qs=Ns.exports,Ps=function(){var e=this,t=e._self._c;return t("div",{staticClass:"steps"},[t("div",{staticClass:"content-container"},e._l(e.contentNodes,(function(n,s){return t(n.component,e._b({key:s,ref:"contentNodes",refInFor:!0,tag:"component",class:e.contentClass(s),attrs:{currentIndex:e.activeStep}},"component",n.props,!1))})),1),e.isBreakpointSmall?e._e():t("BackgroundTheme",{staticClass:"asset-container",class:e.assetContainerClasses},[t("transition",{attrs:{name:"fade"}},[e.visibleAsset.media?t("div",{key:e.visibleAsset.media,class:["asset-wrapper",{ide:e.isTargetIDE}]},[t("Asset",{ref:"asset",staticClass:"step-asset",attrs:{videoAutoplays:"",videoMuted:"",identifier:e.visibleAsset.media}})],1):e._e(),e.visibleAsset.code?t("CodePreview",{attrs:{code:e.visibleAsset.code,preview:e.visibleAsset.runtimePreview,isRuntimePreviewVisible:e.isRuntimePreviewVisible},on:{"runtime-preview-toggle":e.onRuntimePreviewToggle}},[e.visibleAsset.runtimePreview?t("transition",{attrs:{name:"fade"}},[t("Asset",{key:e.visibleAsset.runtimePreview,attrs:{identifier:e.visibleAsset.runtimePreview}})],1):e._e()],1):e._e()],1)],1)],1)},Ds=[],Zs=function(){var e=this,t=e._self._c;return t("div",{class:["code-preview",{ide:e.isTargetIDE}]},[t("CodeTheme",[e.code?t("CodeListing",e._b({attrs:{showLineNumbers:""}},"CodeListing",e.codeProps,!1)):e._e()],1),t("div",{staticClass:"runtime-preview",class:e.runtimePreviewClasses,style:e.previewStyles},[t("div",{staticClass:"runtimve-preview__container"},[t("button",{staticClass:"header",attrs:{disabled:!e.hasRuntimePreview,title:e.runtimePreviewTitle},on:{click:e.togglePreview}},[t("span",{staticClass:"runtime-preview-label",attrs:{"aria-label":e.textAriaLabel}},[e._v(e._s(e.togglePreviewText))]),t("DiagonalArrowIcon",{staticClass:"icon-inline preview-icon",class:[e.shouldDisplayHideLabel?"preview-hide":"preview-show"]})],1),t("transition",{on:{leave:e.handleLeave}},[t("div",{directives:[{name:"show",rawName:"v-show",value:e.shouldDisplayHideLabel,expression:"shouldDisplayHideLabel"}],staticClass:"runtime-preview-asset"},[e._t("default")],2)])],1)])],1)},Rs=[],Os=n(5996),Ls=n(6817),Bs=n(8093);const{BreakpointName:Ms}=o["default"].constants;function Fs({width:e,height:t},n=1){const s=400,i=e<=s?1.75:3;return{width:e/(i/n),height:t/(i/n)}}var Vs={name:"CodePreview",inject:{isTargetIDE:{default:!1},store:{default(){return{state:{references:{}}}}}},components:{DiagonalArrowIcon:Ls.Z,CodeListing:Os.Z,CodeTheme:Bs.Z},props:{code:{type:String,required:!0},preview:{type:String,required:!1},isRuntimePreviewVisible:{type:Boolean,required:!0}},data(){return{tutorialState:this.store.state}},computed:{references:({tutorialState:e})=>e.references,currentBreakpoint(){return this.tutorialState.breakpoint},hasRuntimePreview(){return!!this.preview},previewAssetSize(){const e=this.hasRuntimePreview?this.references[this.preview]:{},t=(e.variants||[{}])[0]||{},n={width:900};let s=t.size||{};s.width||s.height||(s=n);const i=this.currentBreakpoint===Ms.medium?.8:1;return Fs(s,i)},previewSize(){const e={width:102};return this.shouldDisplayHideLabel&&this.previewAssetSize?{width:this.previewAssetSize.width}:e},previewStyles(){const{width:e}=this.previewSize;return{width:`${e}px`}},codeProps(){return this.references[this.code]},runtimePreviewClasses(){return{collapsed:!this.shouldDisplayHideLabel,disabled:!this.hasRuntimePreview,"runtime-preview-ide":this.isTargetIDE}},shouldDisplayHideLabel(){return this.hasRuntimePreview&&this.isRuntimePreviewVisible},runtimePreviewTitle(){return this.hasRuntimePreview?null:this.$t("tutorials.preview.no-preview-available-step")},togglePreviewText(){return this.$tc("tutorials.preview.title",this.hasRuntimePreview?1:0)},textAriaLabel(){return`${this.togglePreviewText}, ${this.shouldDisplayHideLabel?this.$t("verbs.hide"):this.$t("verbs.show")}`}},methods:{handleLeave(e,t){setTimeout(t,200)},togglePreview(){this.hasRuntimePreview&&this.$emit("runtime-preview-toggle",!this.isRuntimePreviewVisible)}}},js=Vs,Es=(0,w.Z)(js,Zs,Rs,!1,null,"395e30cd",null),Hs=Es.exports,Us=n(5657),zs=function(){var e=this,t=e._self._c;return t("div",{style:e.backgroundStyle},[e._t("default")],2)},Gs=[],Ws={name:"BackgroundTheme",data(){return{codeThemeState:_s.Z.state}},computed:{backgroundStyle(){const{codeColors:e}=this.codeThemeState;return e?{"--background":e.background}:null}}},Qs=Ws,Ks=(0,w.Z)(Qs,zs,Gs,!1,null,null,null),Xs=Ks.exports,Ys=function(){var e=this,t=e._self._c;return t("div",{class:["step-container",`step-${e.stepNumber}`]},[t("div",{ref:"step",staticClass:"step",class:{focused:e.isActive},attrs:{"data-index":e.index}},[t("p",{staticClass:"step-label"},[e._v(e._s(e.$t("tutorials.step",{number:e.stepNumber})))]),t("ContentNode",{attrs:{content:e.content}}),e.caption&&e.caption.length>0?t("ContentNode",{staticClass:"caption",attrs:{content:e.caption}}):e._e()],1),e.isBreakpointSmall||!e.isTargetIDE?t("div",{staticClass:"media-container"},[e.media?t("Asset",{attrs:{identifier:e.media,videoAutoplays:"",videoMuted:""}}):e._e(),e.code?t("MobileCodePreview",{attrs:{code:e.code}},[e.runtimePreview?t("Asset",{staticClass:"preview",attrs:{identifier:e.runtimePreview}}):e._e()],1):e._e()],1):e._e()])},Js=[],ei=function(){var e=this,t=e._self._c;return t("BackgroundTheme",{staticClass:"mobile-code-preview"},[e.code?t("GenericModal",{staticClass:"full-code-listing-modal",attrs:{theme:e.isTargetIDE?"code":"light",codeBackgroundColorOverride:e.modalBackgroundColor,isFullscreen:"",visible:e.fullCodeIsVisible},on:{"update:visible":function(t){e.fullCodeIsVisible=t}}},[t("div",{staticClass:"full-code-listing-modal-content"},[t("CodeTheme",[t("CodeListing",e._b({staticClass:"full-code-listing",attrs:{showLineNumbers:""}},"CodeListing",e.codeProps,!1))],1)],1)]):e._e(),t("CodeTheme",[e.code?t("MobileCodeListing",e._b({attrs:{showLineNumbers:""},on:{"file-name-click":e.toggleFullCode}},"MobileCodeListing",e.codeProps,!1)):e._e()],1),t("CodeTheme",{staticClass:"preview-toggle-container"},[t("PreviewToggle",{attrs:{isActionable:!!e.$slots.default},on:{click:e.togglePreview}})],1),e.$slots.default?t("GenericModal",{staticClass:"runtime-preview-modal",attrs:{theme:e.isTargetIDE?"dynamic":"light",isFullscreen:"",visible:e.previewIsVisible},on:{"update:visible":function(t){e.previewIsVisible=t}}},[t("div",{staticClass:"runtime-preview-modal-content"},[t("span",{staticClass:"runtime-preview-label"},[e._v(e._s(e.$tc("tutorials.preview.title",1)))]),e._t("default")],2)]):e._e()],1)},ti=[],ni=function(){var e=this,t=e._self._c;return t("div",{staticClass:"code-listing-preview",attrs:{"data-syntax":e.syntax}},[t("CodeListing",{attrs:{fileName:e.fileName,syntax:e.syntax,fileType:e.fileType,content:e.previewedLines,startLineNumber:e.displayedRange.start,highlights:e.highlights,showLineNumbers:"",isFileNameActionable:""},on:{"file-name-click":function(t){return e.$emit("file-name-click")}}})],1)},si=[],ii={name:"MobileCodeListing",components:{CodeListing:Os.Z},props:{fileName:String,syntax:String,fileType:String,content:{type:Array,required:!0},highlights:{type:Array,default:()=>[]}},computed:{highlightedLineNumbers(){return new Set(this.highlights.map((({line:e})=>e)))},firstHighlightRange(){if(0===this.highlightedLineNumbers.size)return{start:1,end:this.content.length};const e=Math.min(...this.highlightedLineNumbers.values());let t=e;while(this.highlightedLineNumbers.has(t+1))t+=1;return{start:e,end:t}},displayedRange(){const e=this.firstHighlightRange,t=e.start-2<1?1:e.start-2,n=e.end+3>=this.content.length+1?this.content.length+1:e.end+3;return{start:t,end:n}},previewedLines(){return this.content.slice(this.displayedRange.start-1,this.displayedRange.end-1)}}},ri=ii,oi=(0,w.Z)(ri,ni,si,!1,null,"0bdf2f26",null),ai=oi.exports,li=function(){var e=this,t=e._self._c;return t("span",{staticClass:"toggle-preview"},[e.isActionable?t("a",{staticClass:"toggle-text",attrs:{href:"#"},on:{click:function(t){return t.preventDefault(),e.$emit("click")}}},[e._v(" "+e._s(e.$tc("tutorials.preview.title",1))+" "),t("InlinePlusCircleIcon",{staticClass:"toggle-icon icon-inline"})],1):t("span",{staticClass:"toggle-text"},[e._v(" "+e._s(e.$tc("tutorials.preview.title",0))+" ")])])},ci=[],ui=n(6772),di={name:"MobileCodePreviewToggle",components:{InlinePlusCircleIcon:ui.Z},props:{isActionable:{type:Boolean,required:!0}}},pi=di,hi=(0,w.Z)(pi,li,ci,!1,null,"78763c14",null),mi=hi.exports,vi={name:"MobileCodePreview",inject:["isTargetIDE"],mixins:[A.Z],components:{GenericModal:Xt.Z,CodeListing:Os.Z,MobileCodeListing:ai,PreviewToggle:mi,CodeTheme:Bs.Z,BackgroundTheme:Xs},props:{code:{type:String,required:!0}},computed:{codeProps(){return this.references[this.code]},modalBackgroundColor(){const{codeColors:e}=this.store.state;return e?e.background:null}},data(){return{previewIsVisible:!1,fullCodeIsVisible:!1}},methods:{togglePreview(){this.previewIsVisible=!this.previewIsVisible},toggleFullCode(){this.fullCodeIsVisible=!this.fullCodeIsVisible}}},fi=vi,gi=(0,w.Z)(fi,ei,ti,!1,null,"b1691954",null),yi=gi.exports;const{BreakpointName:Ci}=o["default"].constants;var bi={name:"Step",components:{Asset:Ie.Z,MobileCodePreview:yi,ContentNode:qe["default"]},inject:["isTargetIDE","store"],props:{code:{type:String,required:!1},content:{type:Array,required:!0},caption:{type:Array,required:!1},media:{type:String,required:!1},runtimePreview:{type:String,required:!1},sectionNumber:{type:Number,required:!0},stepNumber:{type:Number,required:!0},numberOfSteps:{type:Number,required:!0},index:{type:Number,required:!0},currentIndex:{type:Number,required:!0}},data(){return{tutorialState:this.store.state}},computed:{isBreakpointSmall(){return this.tutorialState.breakpoint===Ci.small},isActive:({index:e,currentIndex:t})=>e===t}},_i=bi,wi=(0,w.Z)(_i,Ys,Js,!1,null,"d0198556",null),ki=wi.exports;const{BreakpointName:Si}=o["default"].constants,{IntersectionDirections:xi}=Xe["default"].constants,Ai="-35% 0% -65% 0%";var Ti={name:"SectionSteps",components:{ContentNode:qe["default"],Step:ki,Asset:Ie.Z,CodePreview:Hs,BackgroundTheme:Xs},mixins:[Xe["default"]],constants:{IntersectionMargins:Ai},inject:["isTargetIDE","store"],data(){const e=this.content.findIndex(this.isStepNode),{code:t,media:n,runtimePreview:s}=this.content[e]||{};return{tutorialState:this.store.state,visibleAsset:{media:n,code:t,runtimePreview:s},activeStep:e}},computed:{assetContainerClasses(){return{"for-step-code":!!this.visibleAsset.code,ide:this.isTargetIDE}},numberOfSteps(){return this.content.filter(this.isStepNode).length},contentNodes(){return this.content.reduce((({stepCounter:e,nodes:t},n,s)=>{const{type:i,...r}=n,o=this.isStepNode(n),a=o?e+1:e;return o?{stepCounter:e+1,nodes:t.concat({component:ki,type:i,props:{...r,stepNumber:a,index:s,numberOfSteps:this.numberOfSteps,sectionNumber:this.sectionNumber}})}:{stepCounter:e,nodes:t.concat({component:qe["default"],type:i,props:{content:[n]}})}}),{stepCounter:0,nodes:[]}).nodes},isBreakpointSmall(){return this.tutorialState.breakpoint===Si.small},stepNodes:({contentNodes:e,isStepNode:t})=>e.filter(t),intersectionRootMargin:()=>Ai},async mounted(){await(0,Us.J)(8),this.findClosestStepNode()},methods:{isStepNode({type:e}){return"step"===e},contentClass(e){return{[`interstitial interstitial-${e+1}`]:!this.isStepNode(this.content[e])}},onReverseIntoLastStep(){const{asset:e}=this.$refs;if(e){const t=e.$el.querySelector("video");t&&(t.currentTime=0,t.play().catch((()=>{})))}},onFocus(e){const{code:t,media:n,runtimePreview:s}=this.content[e];this.activeStep=e,this.visibleAsset={code:t,media:n,runtimePreview:s}},onRuntimePreviewToggle(e){this.$emit("runtime-preview-toggle",e)},findClosestStepNode(){const e=.333*window.innerHeight;let t=null,n=0;this.stepNodes.forEach((s=>{const{index:i}=s.props,r=this.$refs.contentNodes[i].$refs.step;if(!r)return;const{top:o,bottom:a}=r.getBoundingClientRect(),l=o-e,c=a-e,u=Math.abs(l+c);(0===n||u<=n)&&(n=u,t=i)})),null!==t&&this.onFocus(t)},getIntersectionTargets(){const{stepNodes:e,$refs:t}=this;return e.map((({props:{index:e}})=>t.contentNodes[e].$refs.step))},onIntersect(e){const{target:t,isIntersecting:n}=e;if(!n)return;const s=parseFloat(t.getAttribute("data-index"));this.intersectionScrollDirection===xi.down&&s===this.stepNodes[this.stepNodes.length-1].props.index&&this.onReverseIntoLastStep(),this.onFocus(s)}},props:{content:{type:Array,required:!0},isRuntimePreviewVisible:{type:Boolean,require:!0},sectionNumber:{type:Number,required:!0}}},Ii=Ti,$i=(0,w.Z)(Ii,Ps,Ds,!1,null,"e3061a7c",null),Ni=$i.exports,qi={name:"Section",components:{Intro:qs,LinkableSection:tt,Steps:Ni},computed:{introProps(){const[{content:e,media:t},...n]=this.contentSection;return{content:e,expandedSections:n,media:t,sectionAnchor:this.anchor,sectionNumber:this.sectionNumber,title:this.title}}},props:{anchor:{type:String,required:!0},title:{type:String,required:!0},contentSection:{type:Array,required:!0},stepsSection:{type:Array,required:!0},sectionNumber:{type:Number,required:!0},isRuntimePreviewVisible:{type:Boolean,required:!0}},methods:{onRuntimePreviewToggle(e){this.$emit("runtime-preview-toggle",e)}}},Pi=qi,Di=(0,w.Z)(Pi,Ss,xs,!1,null,"6b3a0b3a",null),Zi=Di.exports,Ri={name:"SectionList",components:{Section:Zi},data(){return{isRuntimePreviewVisible:!0}},props:{tasks:{type:Array,required:!0}},methods:{onRuntimePreviewToggle(e){this.isRuntimePreviewVisible=e}}},Oi=Ri,Li=(0,w.Z)(Oi,ws,ks,!1,null,"79a75e9e",null),Bi=Li.exports;const Mi={assessments:cs,hero:Sn,tasks:Bi,callToAction:Pt},Fi=new Set(Object.keys(Mi)),Vi={name:"TutorialSection",render:function(e){const{kind:t,...n}=this.section,s=Mi[t];return s?e(s,{props:n}):null},props:{section:{type:Object,required:!0,validator:e=>Fi.has(e.kind)}}};var ji={name:"Tutorial",mixins:[_e.Z],components:{NavigationBar:be,Section:Vi,PortalTarget:h.YC,BreakpointEmitter:o["default"]},inject:["isTargetIDE","store"],computed:{heroSection(){return this.sections.find((({kind:e})=>"hero"===e))},tutorialTitle(){return(this.heroSection||{}).title},pageTitle(){return this.tutorialTitle?`${this.tutorialTitle} — ${this.metadata.category} Tutorials`:void 0},pageDescription:({heroSection:e,extractFirstParagraphText:t})=>e?t(e.content):null},props:{sections:{type:Array,required:!0},references:{type:Object,required:!0},hierarchy:{type:Object,required:!0},metadata:{type:Object,required:!0},identifierUrl:{type:String,required:!0}},methods:{handleBreakpointChange(e){this.store.updateBreakpoint(e)},handleCodeColorsChange(e){_s.Z.updateCodeColors(e)}},created(){m["default"].setAvailableLocales(this.metadata.availableLocales),this.store.reset(),this.store.setReferences(this.references)},watch:{references(e){this.store.setReferences(e)},"metadata.availableLocales":function(e){m["default"].setAvailableLocales(e)}},mounted(){this.$bridge.on("codeColors",this.handleCodeColorsChange),this.$bridge.send({type:"requestCodeColors"})},beforeDestroy(){this.$bridge.off("codeColors",this.handleCodeColorsChange)}},Ei=ji,Hi=(0,w.Z)(Ei,Cs,bs,!1,null,"70ce4177",null),Ui=Hi.exports,zi=n(1789),Gi=n(5184);const Wi={article:"article",tutorial:"project"};var Qi={name:"Topic",inject:{isTargetIDE:{default:!1}},mixins:[zi.Z,Gi.Z],data(){return{topicData:null}},computed:{navigationBarHeight(){return this.isTargetIDE?0:52},store(){return u},hierarchy(){const{hierarchy:e={}}=this.topicData,{technologyNavigation:t=["overview","tutorials","resources"]}=e||{};return{...e,technologyNavigation:t}},topicKey:({$route:e,topicData:t})=>[e.path,t.identifier.interfaceLanguage].join()},beforeRouteEnter(e,t,n){e.meta.skipFetchingData?n((e=>e.newContentMounted())):(0,r.Ek)(e,t,n).then((e=>n((t=>{t.topicData=e})))).catch(n)},beforeRouteUpdate(e,t,n){(0,r.Us)(e,t)?(0,r.Ek)(e,t,n).then((e=>{this.topicData=e,n()})).catch(n):n()},created(){this.store.reset()},mounted(){this.$bridge.on("contentUpdate",this.handleContentUpdateFromBridge)},beforeDestroy(){this.$bridge.off("contentUpdate",this.handleContentUpdateFromBridge)},methods:{componentFor(e){const{kind:t}=e;return{[Wi.article]:ys,[Wi.tutorial]:Ui}[t]},propsFor(e){const{hierarchy:t,kind:n,metadata:s,references:i,sections:r,identifier:o}=e;return{[Wi.article]:{hierarchy:t,metadata:s,references:i,sections:r,identifierUrl:o.url},[Wi.tutorial]:{hierarchy:t,metadata:s,references:i,sections:r,identifierUrl:o.url}}[n]}},provide(){return{navigationBarHeight:this.navigationBarHeight,store:this.store}},watch:{topicData(){this.$nextTick((()=>{this.newContentMounted()}))}}},Ki=Qi,Xi=(0,w.Z)(Ki,s,i,!1,null,null,null),Yi=Xi.exports}}]); \ No newline at end of file diff --git a/docs/js/tutorials-overview.d4267041.js b/docs/js/tutorials-overview.d4267041.js new file mode 100644 index 0000000..72d242c --- /dev/null +++ b/docs/js/tutorials-overview.d4267041.js @@ -0,0 +1,10 @@ +/*! + * This source file is part of the Swift.org open source project + * + * Copyright (c) 2021 Apple Inc. and the Swift project authors + * Licensed under Apache License v2.0 with Runtime Library Exception + * + * See https://swift.org/LICENSE.txt for license information + * See https://swift.org/CONTRIBUTORS.txt for Swift project authors + */ +"use strict";(self["webpackChunkswift_docc_render"]=self["webpackChunkswift_docc_render"]||[]).push([[843],{7214:function(t,e,n){n.d(e,{Z:function(){return u}});var i=function(){var t=this,e=t._self._c;return e("SVGIcon",{staticClass:"download-icon",attrs:{viewBox:"0 0 14 14",themeId:"download"}},[e("path",{attrs:{d:"M7 0.5c3.59 0 6.5 2.91 6.5 6.5s-2.91 6.5-6.5 6.5c-3.59 0-6.5-2.91-6.5-6.5s2.91-6.5 6.5-6.5zM7 1.5c-3.038 0-5.5 2.462-5.5 5.5s2.462 5.5 5.5 5.5c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5z"}}),e("path",{attrs:{d:"M7.51 2.964l-0.001 5.431 1.308-2.041 0.842 0.539-2.664 4.162-2.633-4.164 0.845-0.534 1.303 2.059 0.001-5.452z"}})])},s=[],o=n(9742),a={name:"DownloadIcon",components:{SVGIcon:o.Z}},r=a,l=n(1001),c=(0,l.Z)(r,i,s,!1,null,null,null),u=c.exports},7181:function(t,e,n){n.d(e,{Z:function(){return u}});var i=function(){var t=this,e=t._self._c;return e("SVGIcon",{staticClass:"inline-close-icon",attrs:{viewBox:"0 0 14 14",themeId:"inline-close"}},[e("path",{attrs:{d:"M11.91 1l1.090 1.090-4.917 4.915 4.906 4.905-1.090 1.090-4.906-4.905-4.892 4.894-1.090-1.090 4.892-4.894-4.903-4.904 1.090-1.090 4.903 4.904z"}})])},s=[],o=n(9742),a={name:"InlineCloseIcon",components:{SVGIcon:o.Z}},r=a,l=n(1001),c=(0,l.Z)(r,i,s,!1,null,null,null),u=c.exports},2573:function(t,e,n){n.d(e,{Z:function(){return c}});var i=function(){var t=this,e=t._self._c;return e("router-link",{staticClass:"nav-title-content",attrs:{to:t.to}},[e("span",{staticClass:"title"},[t._t("default")],2),e("span",{staticClass:"subhead"},[t._v(" "),t._t("subhead")],2)])},s=[],o={name:"NavTitleContainer",props:{to:{type:[String,Object],required:!0}}},a=o,r=n(1001),l=(0,r.Z)(a,i,s,!1,null,"854b4dd6",null),c=l.exports},9732:function(t,e,n){n.d(e,{Z:function(){return c}});var i,s,o={name:"TransitionExpand",functional:!0,render(t,e){const n={props:{name:"expand"},on:{afterEnter(t){t.style.height="auto"},enter(t){const{width:e}=getComputedStyle(t);t.style.width=e,t.style.position="absolute",t.style.visibility="hidden",t.style.height="auto";const{height:n}=getComputedStyle(t);t.style.width=null,t.style.position=null,t.style.visibility=null,t.style.height=0,getComputedStyle(t).height,requestAnimationFrame((()=>{t.style.height=n}))},leave(t){const{height:e}=getComputedStyle(t);t.style.height=e,getComputedStyle(t).height,requestAnimationFrame((()=>{t.style.height=0}))}}};return t("transition",n,e.children)}},a=o,r=n(1001),l=(0,r.Z)(a,i,s,!1,null,null,null),c=l.exports},5199:function(t,e,n){n.r(e),n.d(e,{default:function(){return Ee}});var i=function(){var t=this,e=t._self._c;return t.topicData?e("Overview",t._b({key:t.topicKey},"Overview",t.overviewProps,!1)):t._e()},s=[],o=n(8841),a=n(1789),r=function(){var t=this,e=t._self._c;return e("div",{staticClass:"tutorials-overview"},[t.isTargetIDE?t._e():e("Nav",{staticClass:"theme-dark",attrs:{sections:t.otherSections}},[t._v(" "+t._s(t.title)+" ")]),e("main",{staticClass:"main",attrs:{id:"main",tabindex:"0"}},[e("div",{staticClass:"radial-gradient"},[t._t("above-hero"),t.heroSection?e("Hero",{attrs:{action:t.heroSection.action,content:t.heroSection.content,estimatedTime:t.metadata.estimatedTime,image:t.heroSection.image,title:t.heroSection.title}}):t._e()],2),t.otherSections.length>0?e("LearningPath",{attrs:{sections:t.otherSections}}):t._e()],1)],1)},l=[],c=n(4030),u={state:{activeTutorialLink:null,activeVolume:null,references:{}},reset(){this.state.activeTutorialLink=null,this.state.activeVolume=null,this.state.references={}},setActiveSidebarLink(t){this.state.activeTutorialLink=t},setActiveVolume(t){this.state.activeVolume=t},setReferences(t){this.state.references=t}},m=function(){var t=this,e=t._self._c;return e("NavBase",{scopedSlots:t._u([{key:"menu-items",fn:function(){return[e("NavMenuItemBase",{staticClass:"in-page-navigation"},[e("TutorialsNavigation",{attrs:{sections:t.sections}})],1),t._t("menu-items")]},proxy:!0}],null,!0)},[e("NavTitleContainer",{attrs:{to:t.buildUrl(t.$route.path,t.$route.query)},scopedSlots:t._u([{key:"default",fn:function(){return[t._t("default")]},proxy:!0},{key:"subhead",fn:function(){return[t._v(t._s(t.$tc("tutorials.title",2)))]},proxy:!0}],null,!0)})],1)},d=[],p=n(2586),h=function(){var t=this,e=t._self._c;return e("nav",{staticClass:"tutorials-navigation"},[e("TutorialsNavigationList",t._l(t.sections,(function(n,i){return e("li",{key:`${n.name}_${i}`,class:t.sectionClasses(n)},[t.isVolume(n)?e(t.componentForVolume(n),t._b({tag:"component",on:{"select-menu":t.onSelectMenu,"deselect-menu":t.onDeselectMenu}},"component",t.propsForVolume(n),!1),t._l(n.chapters,(function(n){return e("li",{key:n.name},[e("TutorialsNavigationLink",[t._v(" "+t._s(n.name)+" ")])],1)})),0):t.isResources(n)?e("TutorialsNavigationLink",[t._v(" "+t._s(t.$t("sections.resources"))+" ")]):t._e()],1)})),0)],1)},v=[],f=function(){var t=this,e=t._self._c;return e("router-link",{staticClass:"tutorials-navigation-link",class:{active:t.active},attrs:{to:t.fragment},nativeOn:{click:function(e){return t.handleFocusAndScroll(t.fragment.hash)}}},[t._t("default")],2)},_=[],g=n(3208),C=n(3704),y={name:"TutorialsNavigationLink",mixins:[C.Z],inject:{store:{default:()=>({state:{}})}},data(){return{state:this.store.state}},computed:{active:({state:{activeTutorialLink:t},text:e})=>e===t,fragment:({text:t,$route:e})=>({hash:(0,g.HA)(t),query:e.query}),text:({$slots:{default:[{text:t}]}})=>t.trim()}},b=y,T=n(1001),S=(0,T.Z)(b,f,_,!1,null,"e9f9b59c",null),k=S.exports,V=function(){var t=this,e=t._self._c;return e("ol",{staticClass:"tutorials-navigation-list"},[t._t("default")],2)},Z=[],x={name:"TutorialsNavigationList"},I=x,N=(0,T.Z)(I,V,Z,!1,null,"4e0180fa",null),A=N.exports,w=function(){var t=this,e=t._self._c;return e("div",{staticClass:"tutorials-navigation-menu",class:{collapsed:t.collapsed}},[e("button",{staticClass:"toggle",attrs:{"aria-expanded":t.collapsed?"false":"true",type:"button"},on:{click:function(e){return e.stopPropagation(),t.onClick.apply(null,arguments)}}},[e("span",{staticClass:"text"},[t._v(t._s(t.title))]),e("InlineCloseIcon",{staticClass:"toggle-icon icon-inline"})],1),e("transition-expand",[t.collapsed?t._e():e("div",{staticClass:"tutorials-navigation-menu-content"},[e("TutorialsNavigationList",{attrs:{"aria-label":t.$t("tutorials.nav.chapters")}},[t._t("default")],2)],1)])],1)},q=[],$=n(7181),L=n(9732),M={name:"TutorialsNavigationMenu",components:{InlineCloseIcon:$.Z,TransitionExpand:L.Z,TutorialsNavigationList:A},props:{collapsed:{type:Boolean,default:!0},title:{type:String,required:!0}},methods:{onClick(){this.collapsed?this.$emit("select-menu",this.title):this.$emit("deselect-menu")}}},D=M,F=(0,T.Z)(D,w,q,!1,null,"489416f8",null),R=F.exports;const O={resources:"resources",volume:"volume"};var j={name:"TutorialsNavigation",components:{TutorialsNavigationLink:k,TutorialsNavigationList:A,TutorialsNavigationMenu:R},constants:{SectionKind:O},inject:{store:{default:()=>({setActiveVolume(){}})}},data(){return{state:this.store.state}},props:{sections:{type:Array,required:!0}},computed:{activeVolume:({state:t})=>t.activeVolume},methods:{sectionClasses(t){return{volume:this.isVolume(t),"volume--named":this.isNamedVolume(t),resource:this.isResources(t)}},componentForVolume:({name:t})=>t?R:A,isResources:({kind:t})=>t===O.resources,isVolume:({kind:t})=>t===O.volume,activateFirstNamedVolume(){const{isNamedVolume:t,sections:e}=this,n=e.find(t);n&&this.store.setActiveVolume(n.name)},isNamedVolume(t){return this.isVolume(t)&&t.name},onDeselectMenu(){this.store.setActiveVolume(null)},onSelectMenu(t){this.store.setActiveVolume(t)},propsForVolume({name:t}){const{activeVolume:e}=this;return t?{collapsed:t!==e,title:t}:{"aria-label":"Chapters"}}},created(){this.activateFirstNamedVolume()}},B=j,G=(0,T.Z)(B,h,v,!1,null,"79093ed6",null),H=G.exports,z=n(2573),P=n(2449),E=n(535);const U={resources:"resources",volume:"volume"};var K={name:"Nav",constants:{SectionKind:U},components:{NavMenuItemBase:E.Z,NavTitleContainer:z.Z,TutorialsNavigation:H,NavBase:p.Z},props:{sections:{type:Array,require:!0}},methods:{buildUrl:P.Q2}},Q=K,J=(0,T.Z)(Q,m,d,!1,null,"54bcce6d",null),W=J.exports,X=n(2974),Y=function(){var t=this,e=t._self._c;return e("section",{staticClass:"hero"},[e("div",{staticClass:"copy-container"},[e("h1",{staticClass:"title"},[t._v(t._s(t.title))]),t.content?e("ContentNode",{attrs:{content:t.content}}):t._e(),t.estimatedTime?e("p",{staticClass:"meta"},[e("TimerIcon"),e("span",{staticClass:"meta-content"},[e("strong",{staticClass:"time"},[t._v(t._s(t.estimatedTime))]),e("span",[t._v(" "+t._s(t.$t("tutorials.estimated-time")))])])],1):t._e(),t.action?e("CallToActionButton",{attrs:{action:t.action,"aria-label":t.$t("tutorials.overriding-title",{newTitle:t.action.overridingTitle,title:t.title}),isDark:""}}):t._e()],1),t.image?e("Asset",{attrs:{identifier:t.image}}):t._e()],1)},tt=[],et=n(4655),nt=n(7605),it=n(9519),st=function(){var t=this,e=t._self._c;return e("SVGIcon",{staticClass:"timer-icon",attrs:{viewBox:"0 0 14 14",themeId:"timer"}},[e("path",{attrs:{d:"M7 0.5c3.59 0 6.5 2.91 6.5 6.5s-2.91 6.5-6.5 6.5c-3.59 0-6.5-2.91-6.5-6.5v0c0-3.59 2.91-6.5 6.5-6.5v0zM7 2c-2.761 0-5 2.239-5 5s2.239 5 5 5c2.761 0 5-2.239 5-5v0c0-2.761-2.239-5-5-5v0z"}}),e("path",{attrs:{d:"M6.51 3.51h1.5v3.5h-1.5v-3.5z"}}),e("path",{attrs:{d:"M6.51 7.010h4v1.5h-4v-1.5z"}})])},ot=[],at=n(9742),rt={name:"TimerIcon",components:{SVGIcon:at.Z}},lt=rt,ct=(0,T.Z)(lt,st,ot,!1,null,null,null),ut=ct.exports,mt={name:"Hero",components:{Asset:et.Z,CallToActionButton:nt.Z,ContentNode:it["default"],TimerIcon:ut},props:{action:{type:Object,required:!1},content:{type:Array,required:!1},estimatedTime:{type:String,required:!1},image:{type:String,required:!1},title:{type:String,required:!0}}},dt=mt,pt=(0,T.Z)(dt,Y,tt,!1,null,"383dab71",null),ht=pt.exports,vt=function(){var t=this,e=t._self._c;return e("div",{staticClass:"learning-path",class:t.classes},[e("div",{staticClass:"main-container"},[t.isTargetIDE?t._e():e("div",{staticClass:"secondary-content-container"},[e("TutorialsNavigation",{attrs:{sections:t.sections,"aria-label":t.$t("sections.on-this-page")}})],1),e("div",{staticClass:"primary-content-container"},[e("div",{staticClass:"content-sections-container"},[t._l(t.volumes,(function(n,i){return e("Volume",t._b({key:`volume_${i}`,staticClass:"content-section"},"Volume",t.propsFor(n),!1))})),t._l(t.otherSections,(function(n,i){return e(t.componentFor(n),t._b({key:`resource_${i}`,tag:"component",staticClass:"content-section"},"component",t.propsFor(n),!1))}))],2)])])])},ft=[],_t=function(){var t=this,e=t._self._c;return e("section",{staticClass:"resources",attrs:{id:"resources",tabindex:"-1"}},[e("VolumeName",{attrs:{name:t.$t("sections.resources"),content:t.content}}),e("TileGroup",{attrs:{tiles:t.tiles}})],1)},gt=[],Ct=n(9146);const yt={topOneThird:"-30% 0% -70% 0%",center:"-50% 0% -50% 0%"};var bt={mixins:[Ct["default"]],computed:{intersectionRoot(){return null},intersectionRootMargin(){return yt.center}},methods:{onIntersect(t){if(!t.isIntersecting)return;const e=this.onIntersectViewport;e?e():console.warn("onIntersectViewportCenter not implemented")}}},Tt=function(){var t=this,e=t._self._c;return e("div",{staticClass:"volume-name"},[t.image?e("Asset",{staticClass:"image",attrs:{identifier:t.image,"aria-hidden":"true"}}):t._e(),e("h2",{staticClass:"name"},[t._v(" "+t._s(t.name)+" ")]),t.content?e("ContentNode",{attrs:{content:t.content}}):t._e()],1)},St=[],kt={name:"VolumeName",components:{ContentNode:it["default"],Asset:et.Z},props:{image:{type:String,required:!1},content:{type:Array,required:!1},name:{type:String,required:!1}}},Vt=kt,Zt=(0,T.Z)(Vt,Tt,St,!1,null,"569db166",null),xt=Zt.exports,It=function(){var t=this,e=t._self._c;return e("div",{staticClass:"tile-group",class:t.countClass},t._l(t.tiles,(function(n){return e("Tile",t._b({key:n.title},"Tile",t.propsFor(n),!1))})),1)},Nt=[],At=function(){var t=this,e=t._self._c;return e("div",{staticClass:"tile"},[t.identifier?e("div",{staticClass:"icon"},[e(t.iconComponent,{tag:"component"})],1):t._e(),e("div",{staticClass:"title"},[t._v(t._s(t.title))]),e("ContentNode",{attrs:{content:t.content}}),t.action?e("DestinationDataProvider",{attrs:{destination:t.action},scopedSlots:t._u([{key:"default",fn:function({url:n,title:i}){return[e("Reference",{staticClass:"link",attrs:{url:n}},[t._v(" "+t._s(i)+" "),e("InlineChevronRightIcon",{staticClass:"link-icon icon-inline"})],1)]}}],null,!1,2081312588)}):t._e()],1)},wt=[],qt=n(7775),$t=function(){var t=this,e=t._self._c;return e("SVGIcon",{staticClass:"document-icon",attrs:{viewBox:"0 0 14 14",themeId:"document"}},[e("path",{attrs:{d:"M11.2,5.3,8,2l-.1-.1H2.8V12.1h8.5V6.3l-.1-1ZM8,3.2l2,2.1H8Zm2.4,8H3.6V2.8H7V6.3h3.4Z"}})])},Lt=[],Mt={name:"DocumentIcon",components:{SVGIcon:at.Z}},Dt=Mt,Ft=(0,T.Z)(Dt,$t,Lt,!1,null,"3a80772b",null),Rt=Ft.exports,Ot=n(7214),jt=function(){var t=this,e=t._self._c;return e("SVGIcon",{staticClass:"forum-icon",attrs:{viewBox:"0 0 14 14",themeId:"forum"}},[e("path",{attrs:{d:"M13 1v9h-7l-1.5 3-1.5-3h-2v-9zM12 2h-10v7h1.616l0.884 1.763 0.88-1.763h6.62z"}}),e("path",{attrs:{d:"M3 4h8.001v1h-8.001v-1z"}}),e("path",{attrs:{d:"M3 6h8.001v1h-8.001v-1z"}})])},Bt=[],Gt={name:"ForumIcon",components:{SVGIcon:at.Z}},Ht=Gt,zt=(0,T.Z)(Ht,jt,Bt,!1,null,null,null),Pt=zt.exports,Et=n(6698),Ut=n(9882),Kt=n(8785),Qt=n(1295);const Jt={documentation:"documentation",downloads:"downloads",featured:"featured",forums:"forums",sampleCode:"sampleCode",videos:"videos"};var Wt={name:"Tile",constants:{Identifier:Jt},components:{DestinationDataProvider:Qt.Z,InlineChevronRightIcon:Kt.Z,ContentNode:it["default"],CurlyBracketsIcon:qt.Z,DocumentIcon:Rt,DownloadIcon:Ot.Z,ForumIcon:Pt,PlayIcon:Et.Z,Reference:Ut.Z},props:{action:{type:Object,required:!1},content:{type:Array,required:!0},identifier:{type:String,required:!1},title:{type:String,require:!0}},computed:{iconComponent:({identifier:t})=>({[Jt.documentation]:Rt,[Jt.downloads]:Ot.Z,[Jt.forums]:Pt,[Jt.sampleCode]:qt.Z,[Jt.videos]:Et.Z}[t])}},Xt=Wt,Yt=(0,T.Z)(Xt,At,wt,!1,null,"74dbeb68",null),te=Yt.exports,ee={name:"TileGroup",components:{Tile:te},props:{tiles:{type:Array,required:!0}},computed:{countClass:({tiles:t})=>`count-${t.length}`},methods:{propsFor:({action:t,content:e,identifier:n,title:i})=>({action:t,content:e,identifier:n,title:i})}},ne=ee,ie=(0,T.Z)(ne,It,Nt,!1,null,"4cacce0a",null),se=ie.exports,oe={name:"Resources",mixins:[bt],inject:{store:{default:()=>({setActiveSidebarLink(){},setActiveVolume(){}})}},components:{VolumeName:xt,TileGroup:se},computed:{intersectionRootMargin:()=>yt.topOneThird},props:{content:{type:Array,required:!1},tiles:{type:Array,required:!0}},methods:{onIntersectViewport(){this.store.setActiveSidebarLink("Resources"),this.store.setActiveVolume(null)}}},ae=oe,re=(0,T.Z)(ae,_t,gt,!1,null,"7f8022c1",null),le=re.exports,ce=function(){var t=this,e=t._self._c;return e("section",{staticClass:"volume"},[t.name?e("VolumeName",t._b({},"VolumeName",{name:t.name,image:t.image,content:t.content},!1)):t._e(),t._l(t.chapters,(function(n,i){return e("Chapter",{key:n.name,staticClass:"tile",attrs:{content:n.content,image:n.image,name:n.name,number:i+1,topics:t.lookupTopics(n.tutorials),volumeHasName:!!t.name}})}))],2)},ue=[],me=function(){var t=this,e=t._self._c;return e("section",{staticClass:"chapter",attrs:{id:t.anchor,tabindex:"-1"}},[e("div",{staticClass:"info"},[e("Asset",{attrs:{identifier:t.image,"aria-hidden":"true"}}),e("div",{staticClass:"intro"},[e(t.volumeHasName?"h3":"h2",{tag:"component",staticClass:"name",attrs:{"aria-label":`${t.name} - ${t.$tc("tutorials.sections.chapter",{number:t.number})}`}},[e("span",{staticClass:"eyebrow",attrs:{"aria-hidden":"true"}},[t._v(" "+t._s(t.$t("tutorials.sections.chapter",{number:t.number}))+" ")]),e("span",{staticClass:"name-text",attrs:{"aria-hidden":"true"}},[t._v(t._s(t.name))])]),t.content?e("ContentNode",{attrs:{content:t.content}}):t._e()],1)],1),e("TopicList",{attrs:{topics:t.topics}})],1)},de=[],pe=function(){var t=this,e=t._self._c;return e("ol",{staticClass:"topic-list"},t._l(t.topics,(function(n){return e("li",{key:n.url,staticClass:"topic",class:[t.kindClassFor(n),{"no-time-estimate":!n.estimatedTime}]},[e("div",{staticClass:"topic-icon"},[e(t.iconComponent(n),{tag:"component"})],1),e("router-link",{staticClass:"container",attrs:{to:t.buildUrl(n.url,t.$route.query),"aria-label":t.ariaLabelFor(n)}},[e("div",{staticClass:"link"},[t._v(t._s(n.title))]),n.estimatedTime?e("div",{staticClass:"time"},[e("TimerIcon"),e("span",{staticClass:"time-label"},[t._v(t._s(n.estimatedTime))])],1):t._e()])],1)})),0)},he=[],ve=n(5692),fe=n(8638);const _e={article:"article",tutorial:"project"},ge={article:"article",tutorial:"tutorial"},Ce={[_e.article]:"Article",[_e.tutorial]:"Tutorial"};var ye={name:"ChapterTopicList",components:{TimerIcon:ut},constants:{TopicKind:_e,TopicKindClass:ge,TopicKindIconLabel:Ce},props:{topics:{type:Array,required:!0}},methods:{buildUrl:P.Q2,iconComponent:({kind:t})=>({[_e.article]:ve.Z,[_e.tutorial]:fe.Z}[t]),kindClassFor:({kind:t})=>({[_e.article]:ge.article,[_e.tutorial]:ge.tutorial}[t]),formatTime(t){return t.replace("min",` ${this.$t("tutorials.time.minutes.full")}`).replace("hrs",` ${this.$t("tutorials.time.hours.full")}`)},ariaLabelFor(t){const{title:e,estimatedTime:n,kind:i}=t,s=[e,Ce[i]];return n&&s.push(`${this.formatTime(n)} ${this.$t("tutorials.estimated-time")}`),s.join(" - ")}}},be=ye,Te=(0,T.Z)(be,pe,he,!1,null,"0589dc3b",null),Se=Te.exports,ke={name:"Chapter",mixins:[bt],inject:{store:{default:()=>({setActiveSidebarLink(){},setActiveVolume(){}})}},components:{Asset:et.Z,ContentNode:it["default"],TopicList:Se},props:{content:{type:Array,required:!1},image:{type:String,required:!0},name:{type:String,required:!0},number:{type:Number,required:!0},topics:{type:Array,required:!0},volumeHasName:{type:Boolean,default:!1}},computed:{anchor:({name:t})=>(0,g.HA)(t),intersectionRootMargin:()=>yt.topOneThird},methods:{onIntersectViewport(){this.store.setActiveSidebarLink(this.name),this.volumeHasName||this.store.setActiveVolume(null)}}},Ve=ke,Ze=(0,T.Z)(Ve,me,de,!1,null,"7468bc5e",null),xe=Ze.exports,Ie={name:"Volume",mixins:[bt],components:{VolumeName:xt,Chapter:xe},computed:{references:({store:t})=>t.state.references,intersectionRootMargin:()=>yt.topOneThird},inject:{store:{default:()=>({setActiveVolume(){},state:{references:{}}})}},props:{chapters:{type:Array,required:!0},content:{type:Array,required:!1},image:{type:String,required:!1},name:{type:String,required:!1}},methods:{lookupTopics(t){return t.reduce(((t,e)=>t.concat(this.references[e]||[])),[])},onIntersectViewport(){this.name&&this.store.setActiveVolume(this.name)}}},Ne=Ie,Ae=(0,T.Z)(Ne,ce,ue,!1,null,"540dbf10",null),we=Ae.exports;const qe={resources:"resources",volume:"volume"};var $e={name:"LearningPath",components:{Resources:le,TutorialsNavigation:H,Volume:we},constants:{SectionKind:qe},inject:{isTargetIDE:{default:!1}},props:{sections:{type:Array,required:!0,validator:t=>t.every((t=>Object.prototype.hasOwnProperty.call(qe,t.kind)))}},computed:{classes:({isTargetIDE:t})=>({ide:t}),partitionedSections:({sections:t})=>t.reduce((([t,e],n)=>n.kind===qe.volume?[t.concat(n),e]:[t,e.concat(n)]),[[],[]]),volumes:({partitionedSections:t})=>t[0],otherSections:({partitionedSections:t})=>t[1]},methods:{componentFor:({kind:t})=>({[qe.resources]:le,[qe.volume]:we}[t]),propsFor:({chapters:t,content:e,image:n,kind:i,name:s,tiles:o})=>({[qe.resources]:{content:e,tiles:o},[qe.volume]:{chapters:t,content:e,image:n,name:s}}[i])}},Le=$e,Me=(0,T.Z)(Le,vt,ft,!1,null,"69a72bbc",null),De=Me.exports;const Fe={hero:"hero",resources:"resources",volume:"volume"};var Re={name:"TutorialsOverview",components:{Hero:ht,LearningPath:De,Nav:W},mixins:[X.Z],constants:{SectionKind:Fe},inject:{isTargetIDE:{default:!1}},props:{metadata:{type:Object,default:()=>({})},references:{type:Object,default:()=>({})},sections:{type:Array,default:()=>[],validator:t=>t.every((t=>Object.prototype.hasOwnProperty.call(Fe,t.kind)))}},computed:{pageTitle:({title:t})=>[t,"Tutorials"].filter(Boolean).join(" "),pageDescription:({heroSection:t,extractFirstParagraphText:e})=>t?e(t.content):null,partitionedSections:({sections:t})=>t.reduce((([t,e],n)=>n.kind===Fe.hero?[t.concat(n),e]:[t,e.concat(n)]),[[],[]]),heroSections:({partitionedSections:t})=>t[0],otherSections:({partitionedSections:t})=>t[1],heroSection:({heroSections:t})=>t[0],store:()=>u,title:({metadata:{category:t=""}})=>t},provide(){return{store:this.store}},created(){c["default"].setAvailableLocales(this.metadata.availableLocales),this.store.reset(),this.store.setReferences(this.references)},watch:{references(t){this.store.setReferences(t)},"metadata.availableLocales":function(t){c["default"].setAvailableLocales(t)}}},Oe=Re,je=(0,T.Z)(Oe,r,l,!1,null,"40c62c57",null),Be=je.exports,Ge=n(5184),He={name:"TutorialsOverview",components:{Overview:Be},mixins:[a.Z,Ge.Z],data(){return{topicData:null}},computed:{overviewProps:({topicData:{metadata:t,references:e,sections:n}})=>({metadata:t,references:e,sections:n}),topicKey:({$route:t,topicData:e})=>[t.path,e.identifier.interfaceLanguage].join()},beforeRouteEnter(t,e,n){t.meta.skipFetchingData?n((t=>t.newContentMounted())):(0,o.Ek)(t,e,n).then((t=>n((e=>{e.topicData=t})))).catch(n)},beforeRouteUpdate(t,e,n){(0,o.Us)(t,e)?(0,o.Ek)(t,e,n).then((t=>{this.topicData=t,n()})).catch(n):n()},mounted(){this.$bridge.on("contentUpdate",this.handleContentUpdateFromBridge)},beforeDestroy(){this.$bridge.off("contentUpdate",this.handleContentUpdateFromBridge)},watch:{topicData(){this.$nextTick((()=>{this.newContentMounted()}))}}},ze=He,Pe=(0,T.Z)(ze,i,s,!1,null,null,null),Ee=Pe.exports}}]); \ No newline at end of file diff --git a/docs/metadata.json b/docs/metadata.json new file mode 100644 index 0000000..7c18094 --- /dev/null +++ b/docs/metadata.json @@ -0,0 +1 @@ +{"schemaVersion":{"patch":0,"minor":1,"major":0},"bundleIdentifier":"AriNote","bundleDisplayName":"AriNote"} \ No newline at end of file