├── README.md ├── SwiftUI-List-Pagination.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── mataraishinonome.io.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── SwiftUI-List-Pagination ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── Base.lproj │ └── LaunchScreen.storyboard ├── ContentView.swift ├── FetchData.swift ├── Info.plist ├── ListRow.swift ├── Loading │ ├── LoadingRow.swift │ └── LoadingView.swift ├── Model │ ├── RowData.swift │ └── RowDataViewModel.swift ├── Preview Content │ └── Preview Assets.xcassets │ │ └── Contents.json ├── RequestError.swift └── SceneDelegate.swift ├── SwiftUI-List-PaginationTests ├── Info.plist └── SwiftUI_List_PaginationTests.swift └── SwiftUI-List-PaginationUITests ├── Info.plist └── SwiftUI_List_PaginationUITests.swift /README.md: -------------------------------------------------------------------------------- 1 | # SwiftUI-List-Pagination 2 | ![platform](https://img.shields.io/badge/platform-ios-blue.svg) ![ios](https://img.shields.io/badge/ios-13.0%2B-blue.svg) ![swift](https://img.shields.io/badge/SwiftUI(beta)-orange.svg) ![Swift](https://img.shields.io/badge/Swift(beta)-5.1-brightgreen.svg) ![twitter](https://img.shields.io/badge/twitter-@mcz9mm-blue.svg) 3 | 4 | This is a sample application of LIST that implements pagination with loading 5 | 6 | ## Requirement 7 | - Xcode 11 beta 8 | - iOS 13.0 beta 9 | - SwiftUI 10 | 11 | ## Screenshot 12 | ![Jun-17-2019 09-23-52](https://user-images.githubusercontent.com/11751495/59571630-be85ec80-90e1-11e9-91c2-9339e9cab98a.gif) 13 | ![1](https://user-images.githubusercontent.com/11751495/59571682-381dda80-90e2-11e9-9929-06a8538e617c.jpg) 14 | 15 | ## Detail 16 | 17 | ### Loading View 18 | 19 | use UIViewRepresentable 20 | 21 | ```swift 22 | struct LoadingView: UIViewRepresentable { 23 | 24 | var isLoading: Bool 25 | 26 | func makeUIView(context: Context) -> UIActivityIndicatorView { 27 | let indicator = UIActivityIndicatorView(frame: .zero) 28 | indicator.style = .large 29 | indicator.hidesWhenStopped = true 30 | return indicator 31 | } 32 | 33 | func updateUIView(_ view: UIActivityIndicatorView, context: Context) { 34 | if self.isLoading { 35 | view.startAnimating() 36 | } else { 37 | view.stopAnimating() 38 | } 39 | } 40 | } 41 | ``` 42 | 43 | LoadingRow 44 | 45 | ```swift 46 | struct LoadingRow : View { 47 | @State var isLoading: Bool 48 | 49 | var body: some View { 50 | HStack { 51 | Spacer() 52 | LoadingView(isLoading: isLoading) 53 | Spacer() 54 | } 55 | } 56 | } 57 | ``` 58 | 59 | 60 | ## License 61 | SwiftUI-List-Pagination is released the MIT license. 62 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3F6933A022B633F6005D7963 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F69339F22B633F6005D7963 /* AppDelegate.swift */; }; 11 | 3F6933A222B633F6005D7963 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6933A122B633F6005D7963 /* SceneDelegate.swift */; }; 12 | 3F6933A422B633F6005D7963 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6933A322B633F6005D7963 /* ContentView.swift */; }; 13 | 3F6933A622B633F9005D7963 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3F6933A522B633F9005D7963 /* Assets.xcassets */; }; 14 | 3F6933A922B633F9005D7963 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3F6933A822B633F9005D7963 /* Preview Assets.xcassets */; }; 15 | 3F6933AC22B633F9005D7963 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3F6933AA22B633F9005D7963 /* LaunchScreen.storyboard */; }; 16 | 3F6933B722B633F9005D7963 /* SwiftUI_List_PaginationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6933B622B633F9005D7963 /* SwiftUI_List_PaginationTests.swift */; }; 17 | 3F6933C222B633F9005D7963 /* SwiftUI_List_PaginationUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6933C122B633F9005D7963 /* SwiftUI_List_PaginationUITests.swift */; }; 18 | 3F6933D022B63425005D7963 /* RowData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6933CF22B63425005D7963 /* RowData.swift */; }; 19 | 3F6933D322B634E9005D7963 /* RowDataViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6933D222B634E9005D7963 /* RowDataViewModel.swift */; }; 20 | 3F6933D522B635BC005D7963 /* RequestError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6933D422B635BC005D7963 /* RequestError.swift */; }; 21 | 3F6933D722B635D9005D7963 /* FetchData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6933D622B635D9005D7963 /* FetchData.swift */; }; 22 | 3F6933D922B63A7A005D7963 /* ListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6933D822B63A7A005D7963 /* ListRow.swift */; }; 23 | 3F6933DC22B63B4F005D7963 /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6933DB22B63B4F005D7963 /* LoadingView.swift */; }; 24 | 3F6933DE22B63B6F005D7963 /* LoadingRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F6933DD22B63B6F005D7963 /* LoadingRow.swift */; }; 25 | /* End PBXBuildFile section */ 26 | 27 | /* Begin PBXContainerItemProxy section */ 28 | 3F6933B322B633F9005D7963 /* PBXContainerItemProxy */ = { 29 | isa = PBXContainerItemProxy; 30 | containerPortal = 3F69339422B633F6005D7963 /* Project object */; 31 | proxyType = 1; 32 | remoteGlobalIDString = 3F69339B22B633F6005D7963; 33 | remoteInfo = "SwiftUI-List-Pagination"; 34 | }; 35 | 3F6933BE22B633F9005D7963 /* PBXContainerItemProxy */ = { 36 | isa = PBXContainerItemProxy; 37 | containerPortal = 3F69339422B633F6005D7963 /* Project object */; 38 | proxyType = 1; 39 | remoteGlobalIDString = 3F69339B22B633F6005D7963; 40 | remoteInfo = "SwiftUI-List-Pagination"; 41 | }; 42 | /* End PBXContainerItemProxy section */ 43 | 44 | /* Begin PBXFileReference section */ 45 | 3F69339C22B633F6005D7963 /* SwiftUI-List-Pagination.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "SwiftUI-List-Pagination.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 46 | 3F69339F22B633F6005D7963 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 47 | 3F6933A122B633F6005D7963 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 48 | 3F6933A322B633F6005D7963 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 49 | 3F6933A522B633F9005D7963 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 50 | 3F6933A822B633F9005D7963 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 51 | 3F6933AB22B633F9005D7963 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 52 | 3F6933AD22B633F9005D7963 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | 3F6933B222B633F9005D7963 /* SwiftUI-List-PaginationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SwiftUI-List-PaginationTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | 3F6933B622B633F9005D7963 /* SwiftUI_List_PaginationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUI_List_PaginationTests.swift; sourceTree = ""; }; 55 | 3F6933B822B633F9005D7963 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | 3F6933BD22B633F9005D7963 /* SwiftUI-List-PaginationUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SwiftUI-List-PaginationUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 3F6933C122B633F9005D7963 /* SwiftUI_List_PaginationUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUI_List_PaginationUITests.swift; sourceTree = ""; }; 58 | 3F6933C322B633F9005D7963 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 59 | 3F6933CF22B63425005D7963 /* RowData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RowData.swift; sourceTree = ""; }; 60 | 3F6933D222B634E9005D7963 /* RowDataViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RowDataViewModel.swift; sourceTree = ""; }; 61 | 3F6933D422B635BC005D7963 /* RequestError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestError.swift; sourceTree = ""; }; 62 | 3F6933D622B635D9005D7963 /* FetchData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchData.swift; sourceTree = ""; }; 63 | 3F6933D822B63A7A005D7963 /* ListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListRow.swift; sourceTree = ""; }; 64 | 3F6933DB22B63B4F005D7963 /* LoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = ""; }; 65 | 3F6933DD22B63B6F005D7963 /* LoadingRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingRow.swift; sourceTree = ""; }; 66 | /* End PBXFileReference section */ 67 | 68 | /* Begin PBXFrameworksBuildPhase section */ 69 | 3F69339922B633F6005D7963 /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | ); 74 | runOnlyForDeploymentPostprocessing = 0; 75 | }; 76 | 3F6933AF22B633F9005D7963 /* Frameworks */ = { 77 | isa = PBXFrameworksBuildPhase; 78 | buildActionMask = 2147483647; 79 | files = ( 80 | ); 81 | runOnlyForDeploymentPostprocessing = 0; 82 | }; 83 | 3F6933BA22B633F9005D7963 /* Frameworks */ = { 84 | isa = PBXFrameworksBuildPhase; 85 | buildActionMask = 2147483647; 86 | files = ( 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | /* End PBXFrameworksBuildPhase section */ 91 | 92 | /* Begin PBXGroup section */ 93 | 3F69339322B633F6005D7963 = { 94 | isa = PBXGroup; 95 | children = ( 96 | 3F69339E22B633F6005D7963 /* SwiftUI-List-Pagination */, 97 | 3F6933B522B633F9005D7963 /* SwiftUI-List-PaginationTests */, 98 | 3F6933C022B633F9005D7963 /* SwiftUI-List-PaginationUITests */, 99 | 3F69339D22B633F6005D7963 /* Products */, 100 | ); 101 | sourceTree = ""; 102 | }; 103 | 3F69339D22B633F6005D7963 /* Products */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 3F69339C22B633F6005D7963 /* SwiftUI-List-Pagination.app */, 107 | 3F6933B222B633F9005D7963 /* SwiftUI-List-PaginationTests.xctest */, 108 | 3F6933BD22B633F9005D7963 /* SwiftUI-List-PaginationUITests.xctest */, 109 | ); 110 | name = Products; 111 | sourceTree = ""; 112 | }; 113 | 3F69339E22B633F6005D7963 /* SwiftUI-List-Pagination */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | 3F69339F22B633F6005D7963 /* AppDelegate.swift */, 117 | 3F6933D122B6342D005D7963 /* Model */, 118 | 3F6933DA22B63B2B005D7963 /* Loading */, 119 | 3F6933A122B633F6005D7963 /* SceneDelegate.swift */, 120 | 3F6933D422B635BC005D7963 /* RequestError.swift */, 121 | 3F6933D622B635D9005D7963 /* FetchData.swift */, 122 | 3F6933D822B63A7A005D7963 /* ListRow.swift */, 123 | 3F6933A322B633F6005D7963 /* ContentView.swift */, 124 | 3F6933A522B633F9005D7963 /* Assets.xcassets */, 125 | 3F6933AA22B633F9005D7963 /* LaunchScreen.storyboard */, 126 | 3F6933AD22B633F9005D7963 /* Info.plist */, 127 | 3F6933A722B633F9005D7963 /* Preview Content */, 128 | ); 129 | path = "SwiftUI-List-Pagination"; 130 | sourceTree = ""; 131 | }; 132 | 3F6933A722B633F9005D7963 /* Preview Content */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 3F6933A822B633F9005D7963 /* Preview Assets.xcassets */, 136 | ); 137 | path = "Preview Content"; 138 | sourceTree = ""; 139 | }; 140 | 3F6933B522B633F9005D7963 /* SwiftUI-List-PaginationTests */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 3F6933B622B633F9005D7963 /* SwiftUI_List_PaginationTests.swift */, 144 | 3F6933B822B633F9005D7963 /* Info.plist */, 145 | ); 146 | path = "SwiftUI-List-PaginationTests"; 147 | sourceTree = ""; 148 | }; 149 | 3F6933C022B633F9005D7963 /* SwiftUI-List-PaginationUITests */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | 3F6933C122B633F9005D7963 /* SwiftUI_List_PaginationUITests.swift */, 153 | 3F6933C322B633F9005D7963 /* Info.plist */, 154 | ); 155 | path = "SwiftUI-List-PaginationUITests"; 156 | sourceTree = ""; 157 | }; 158 | 3F6933D122B6342D005D7963 /* Model */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 3F6933CF22B63425005D7963 /* RowData.swift */, 162 | 3F6933D222B634E9005D7963 /* RowDataViewModel.swift */, 163 | ); 164 | path = Model; 165 | sourceTree = ""; 166 | }; 167 | 3F6933DA22B63B2B005D7963 /* Loading */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | 3F6933DB22B63B4F005D7963 /* LoadingView.swift */, 171 | 3F6933DD22B63B6F005D7963 /* LoadingRow.swift */, 172 | ); 173 | path = Loading; 174 | sourceTree = ""; 175 | }; 176 | /* End PBXGroup section */ 177 | 178 | /* Begin PBXNativeTarget section */ 179 | 3F69339B22B633F6005D7963 /* SwiftUI-List-Pagination */ = { 180 | isa = PBXNativeTarget; 181 | buildConfigurationList = 3F6933C622B633F9005D7963 /* Build configuration list for PBXNativeTarget "SwiftUI-List-Pagination" */; 182 | buildPhases = ( 183 | 3F69339822B633F6005D7963 /* Sources */, 184 | 3F69339922B633F6005D7963 /* Frameworks */, 185 | 3F69339A22B633F6005D7963 /* Resources */, 186 | ); 187 | buildRules = ( 188 | ); 189 | dependencies = ( 190 | ); 191 | name = "SwiftUI-List-Pagination"; 192 | productName = "SwiftUI-List-Pagination"; 193 | productReference = 3F69339C22B633F6005D7963 /* SwiftUI-List-Pagination.app */; 194 | productType = "com.apple.product-type.application"; 195 | }; 196 | 3F6933B122B633F9005D7963 /* SwiftUI-List-PaginationTests */ = { 197 | isa = PBXNativeTarget; 198 | buildConfigurationList = 3F6933C922B633F9005D7963 /* Build configuration list for PBXNativeTarget "SwiftUI-List-PaginationTests" */; 199 | buildPhases = ( 200 | 3F6933AE22B633F9005D7963 /* Sources */, 201 | 3F6933AF22B633F9005D7963 /* Frameworks */, 202 | 3F6933B022B633F9005D7963 /* Resources */, 203 | ); 204 | buildRules = ( 205 | ); 206 | dependencies = ( 207 | 3F6933B422B633F9005D7963 /* PBXTargetDependency */, 208 | ); 209 | name = "SwiftUI-List-PaginationTests"; 210 | productName = "SwiftUI-List-PaginationTests"; 211 | productReference = 3F6933B222B633F9005D7963 /* SwiftUI-List-PaginationTests.xctest */; 212 | productType = "com.apple.product-type.bundle.unit-test"; 213 | }; 214 | 3F6933BC22B633F9005D7963 /* SwiftUI-List-PaginationUITests */ = { 215 | isa = PBXNativeTarget; 216 | buildConfigurationList = 3F6933CC22B633F9005D7963 /* Build configuration list for PBXNativeTarget "SwiftUI-List-PaginationUITests" */; 217 | buildPhases = ( 218 | 3F6933B922B633F9005D7963 /* Sources */, 219 | 3F6933BA22B633F9005D7963 /* Frameworks */, 220 | 3F6933BB22B633F9005D7963 /* Resources */, 221 | ); 222 | buildRules = ( 223 | ); 224 | dependencies = ( 225 | 3F6933BF22B633F9005D7963 /* PBXTargetDependency */, 226 | ); 227 | name = "SwiftUI-List-PaginationUITests"; 228 | productName = "SwiftUI-List-PaginationUITests"; 229 | productReference = 3F6933BD22B633F9005D7963 /* SwiftUI-List-PaginationUITests.xctest */; 230 | productType = "com.apple.product-type.bundle.ui-testing"; 231 | }; 232 | /* End PBXNativeTarget section */ 233 | 234 | /* Begin PBXProject section */ 235 | 3F69339422B633F6005D7963 /* Project object */ = { 236 | isa = PBXProject; 237 | attributes = { 238 | LastSwiftUpdateCheck = 1100; 239 | LastUpgradeCheck = 1100; 240 | ORGANIZATIONNAME = MataraiKaoru; 241 | TargetAttributes = { 242 | 3F69339B22B633F6005D7963 = { 243 | CreatedOnToolsVersion = 11.0; 244 | }; 245 | 3F6933B122B633F9005D7963 = { 246 | CreatedOnToolsVersion = 11.0; 247 | TestTargetID = 3F69339B22B633F6005D7963; 248 | }; 249 | 3F6933BC22B633F9005D7963 = { 250 | CreatedOnToolsVersion = 11.0; 251 | TestTargetID = 3F69339B22B633F6005D7963; 252 | }; 253 | }; 254 | }; 255 | buildConfigurationList = 3F69339722B633F6005D7963 /* Build configuration list for PBXProject "SwiftUI-List-Pagination" */; 256 | compatibilityVersion = "Xcode 9.3"; 257 | developmentRegion = en; 258 | hasScannedForEncodings = 0; 259 | knownRegions = ( 260 | en, 261 | Base, 262 | ); 263 | mainGroup = 3F69339322B633F6005D7963; 264 | productRefGroup = 3F69339D22B633F6005D7963 /* Products */; 265 | projectDirPath = ""; 266 | projectRoot = ""; 267 | targets = ( 268 | 3F69339B22B633F6005D7963 /* SwiftUI-List-Pagination */, 269 | 3F6933B122B633F9005D7963 /* SwiftUI-List-PaginationTests */, 270 | 3F6933BC22B633F9005D7963 /* SwiftUI-List-PaginationUITests */, 271 | ); 272 | }; 273 | /* End PBXProject section */ 274 | 275 | /* Begin PBXResourcesBuildPhase section */ 276 | 3F69339A22B633F6005D7963 /* Resources */ = { 277 | isa = PBXResourcesBuildPhase; 278 | buildActionMask = 2147483647; 279 | files = ( 280 | 3F6933AC22B633F9005D7963 /* LaunchScreen.storyboard in Resources */, 281 | 3F6933A922B633F9005D7963 /* Preview Assets.xcassets in Resources */, 282 | 3F6933A622B633F9005D7963 /* Assets.xcassets in Resources */, 283 | ); 284 | runOnlyForDeploymentPostprocessing = 0; 285 | }; 286 | 3F6933B022B633F9005D7963 /* Resources */ = { 287 | isa = PBXResourcesBuildPhase; 288 | buildActionMask = 2147483647; 289 | files = ( 290 | ); 291 | runOnlyForDeploymentPostprocessing = 0; 292 | }; 293 | 3F6933BB22B633F9005D7963 /* Resources */ = { 294 | isa = PBXResourcesBuildPhase; 295 | buildActionMask = 2147483647; 296 | files = ( 297 | ); 298 | runOnlyForDeploymentPostprocessing = 0; 299 | }; 300 | /* End PBXResourcesBuildPhase section */ 301 | 302 | /* Begin PBXSourcesBuildPhase section */ 303 | 3F69339822B633F6005D7963 /* Sources */ = { 304 | isa = PBXSourcesBuildPhase; 305 | buildActionMask = 2147483647; 306 | files = ( 307 | 3F6933A022B633F6005D7963 /* AppDelegate.swift in Sources */, 308 | 3F6933D722B635D9005D7963 /* FetchData.swift in Sources */, 309 | 3F6933D022B63425005D7963 /* RowData.swift in Sources */, 310 | 3F6933D522B635BC005D7963 /* RequestError.swift in Sources */, 311 | 3F6933D922B63A7A005D7963 /* ListRow.swift in Sources */, 312 | 3F6933DE22B63B6F005D7963 /* LoadingRow.swift in Sources */, 313 | 3F6933A222B633F6005D7963 /* SceneDelegate.swift in Sources */, 314 | 3F6933D322B634E9005D7963 /* RowDataViewModel.swift in Sources */, 315 | 3F6933A422B633F6005D7963 /* ContentView.swift in Sources */, 316 | 3F6933DC22B63B4F005D7963 /* LoadingView.swift in Sources */, 317 | ); 318 | runOnlyForDeploymentPostprocessing = 0; 319 | }; 320 | 3F6933AE22B633F9005D7963 /* Sources */ = { 321 | isa = PBXSourcesBuildPhase; 322 | buildActionMask = 2147483647; 323 | files = ( 324 | 3F6933B722B633F9005D7963 /* SwiftUI_List_PaginationTests.swift in Sources */, 325 | ); 326 | runOnlyForDeploymentPostprocessing = 0; 327 | }; 328 | 3F6933B922B633F9005D7963 /* Sources */ = { 329 | isa = PBXSourcesBuildPhase; 330 | buildActionMask = 2147483647; 331 | files = ( 332 | 3F6933C222B633F9005D7963 /* SwiftUI_List_PaginationUITests.swift in Sources */, 333 | ); 334 | runOnlyForDeploymentPostprocessing = 0; 335 | }; 336 | /* End PBXSourcesBuildPhase section */ 337 | 338 | /* Begin PBXTargetDependency section */ 339 | 3F6933B422B633F9005D7963 /* PBXTargetDependency */ = { 340 | isa = PBXTargetDependency; 341 | target = 3F69339B22B633F6005D7963 /* SwiftUI-List-Pagination */; 342 | targetProxy = 3F6933B322B633F9005D7963 /* PBXContainerItemProxy */; 343 | }; 344 | 3F6933BF22B633F9005D7963 /* PBXTargetDependency */ = { 345 | isa = PBXTargetDependency; 346 | target = 3F69339B22B633F6005D7963 /* SwiftUI-List-Pagination */; 347 | targetProxy = 3F6933BE22B633F9005D7963 /* PBXContainerItemProxy */; 348 | }; 349 | /* End PBXTargetDependency section */ 350 | 351 | /* Begin PBXVariantGroup section */ 352 | 3F6933AA22B633F9005D7963 /* LaunchScreen.storyboard */ = { 353 | isa = PBXVariantGroup; 354 | children = ( 355 | 3F6933AB22B633F9005D7963 /* Base */, 356 | ); 357 | name = LaunchScreen.storyboard; 358 | sourceTree = ""; 359 | }; 360 | /* End PBXVariantGroup section */ 361 | 362 | /* Begin XCBuildConfiguration section */ 363 | 3F6933C422B633F9005D7963 /* Debug */ = { 364 | isa = XCBuildConfiguration; 365 | buildSettings = { 366 | ALWAYS_SEARCH_USER_PATHS = NO; 367 | CLANG_ANALYZER_NONNULL = YES; 368 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 369 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 370 | CLANG_CXX_LIBRARY = "libc++"; 371 | CLANG_ENABLE_MODULES = YES; 372 | CLANG_ENABLE_OBJC_ARC = YES; 373 | CLANG_ENABLE_OBJC_WEAK = YES; 374 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 375 | CLANG_WARN_BOOL_CONVERSION = YES; 376 | CLANG_WARN_COMMA = YES; 377 | CLANG_WARN_CONSTANT_CONVERSION = YES; 378 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 379 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 380 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 381 | CLANG_WARN_EMPTY_BODY = YES; 382 | CLANG_WARN_ENUM_CONVERSION = YES; 383 | CLANG_WARN_INFINITE_RECURSION = YES; 384 | CLANG_WARN_INT_CONVERSION = YES; 385 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 386 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 387 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 388 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 389 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 390 | CLANG_WARN_STRICT_PROTOTYPES = YES; 391 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 392 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 393 | CLANG_WARN_UNREACHABLE_CODE = YES; 394 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 395 | COPY_PHASE_STRIP = NO; 396 | DEBUG_INFORMATION_FORMAT = dwarf; 397 | ENABLE_STRICT_OBJC_MSGSEND = YES; 398 | ENABLE_TESTABILITY = YES; 399 | GCC_C_LANGUAGE_STANDARD = gnu11; 400 | GCC_DYNAMIC_NO_PIC = NO; 401 | GCC_NO_COMMON_BLOCKS = YES; 402 | GCC_OPTIMIZATION_LEVEL = 0; 403 | GCC_PREPROCESSOR_DEFINITIONS = ( 404 | "DEBUG=1", 405 | "$(inherited)", 406 | ); 407 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 408 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 409 | GCC_WARN_UNDECLARED_SELECTOR = YES; 410 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 411 | GCC_WARN_UNUSED_FUNCTION = YES; 412 | GCC_WARN_UNUSED_VARIABLE = YES; 413 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 414 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 415 | MTL_FAST_MATH = YES; 416 | ONLY_ACTIVE_ARCH = YES; 417 | SDKROOT = iphoneos; 418 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 419 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 420 | }; 421 | name = Debug; 422 | }; 423 | 3F6933C522B633F9005D7963 /* Release */ = { 424 | isa = XCBuildConfiguration; 425 | buildSettings = { 426 | ALWAYS_SEARCH_USER_PATHS = NO; 427 | CLANG_ANALYZER_NONNULL = YES; 428 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 429 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 430 | CLANG_CXX_LIBRARY = "libc++"; 431 | CLANG_ENABLE_MODULES = YES; 432 | CLANG_ENABLE_OBJC_ARC = YES; 433 | CLANG_ENABLE_OBJC_WEAK = YES; 434 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 435 | CLANG_WARN_BOOL_CONVERSION = YES; 436 | CLANG_WARN_COMMA = YES; 437 | CLANG_WARN_CONSTANT_CONVERSION = YES; 438 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 439 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 440 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 441 | CLANG_WARN_EMPTY_BODY = YES; 442 | CLANG_WARN_ENUM_CONVERSION = YES; 443 | CLANG_WARN_INFINITE_RECURSION = YES; 444 | CLANG_WARN_INT_CONVERSION = YES; 445 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 446 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 447 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 448 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 449 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 450 | CLANG_WARN_STRICT_PROTOTYPES = YES; 451 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 452 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 453 | CLANG_WARN_UNREACHABLE_CODE = YES; 454 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 455 | COPY_PHASE_STRIP = NO; 456 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 457 | ENABLE_NS_ASSERTIONS = NO; 458 | ENABLE_STRICT_OBJC_MSGSEND = YES; 459 | GCC_C_LANGUAGE_STANDARD = gnu11; 460 | GCC_NO_COMMON_BLOCKS = YES; 461 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 462 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 463 | GCC_WARN_UNDECLARED_SELECTOR = YES; 464 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 465 | GCC_WARN_UNUSED_FUNCTION = YES; 466 | GCC_WARN_UNUSED_VARIABLE = YES; 467 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 468 | MTL_ENABLE_DEBUG_INFO = NO; 469 | MTL_FAST_MATH = YES; 470 | SDKROOT = iphoneos; 471 | SWIFT_COMPILATION_MODE = wholemodule; 472 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 473 | VALIDATE_PRODUCT = YES; 474 | }; 475 | name = Release; 476 | }; 477 | 3F6933C722B633F9005D7963 /* Debug */ = { 478 | isa = XCBuildConfiguration; 479 | buildSettings = { 480 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 481 | CODE_SIGN_STYLE = Automatic; 482 | DEVELOPMENT_ASSET_PATHS = "SwiftUI-List-Pagination/Preview\\ Content"; 483 | DEVELOPMENT_TEAM = PA3RF24U47; 484 | ENABLE_PREVIEWS = YES; 485 | INFOPLIST_FILE = "SwiftUI-List-Pagination/Info.plist"; 486 | LD_RUNPATH_SEARCH_PATHS = ( 487 | "$(inherited)", 488 | "@executable_path/Frameworks", 489 | ); 490 | PRODUCT_BUNDLE_IDENTIFIER = "mcz9mm.SwiftUI-List-Pagination"; 491 | PRODUCT_NAME = "$(TARGET_NAME)"; 492 | SWIFT_VERSION = 5.0; 493 | TARGETED_DEVICE_FAMILY = "1,2"; 494 | }; 495 | name = Debug; 496 | }; 497 | 3F6933C822B633F9005D7963 /* Release */ = { 498 | isa = XCBuildConfiguration; 499 | buildSettings = { 500 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 501 | CODE_SIGN_STYLE = Automatic; 502 | DEVELOPMENT_ASSET_PATHS = "SwiftUI-List-Pagination/Preview\\ Content"; 503 | DEVELOPMENT_TEAM = PA3RF24U47; 504 | ENABLE_PREVIEWS = YES; 505 | INFOPLIST_FILE = "SwiftUI-List-Pagination/Info.plist"; 506 | LD_RUNPATH_SEARCH_PATHS = ( 507 | "$(inherited)", 508 | "@executable_path/Frameworks", 509 | ); 510 | PRODUCT_BUNDLE_IDENTIFIER = "mcz9mm.SwiftUI-List-Pagination"; 511 | PRODUCT_NAME = "$(TARGET_NAME)"; 512 | SWIFT_VERSION = 5.0; 513 | TARGETED_DEVICE_FAMILY = "1,2"; 514 | }; 515 | name = Release; 516 | }; 517 | 3F6933CA22B633F9005D7963 /* Debug */ = { 518 | isa = XCBuildConfiguration; 519 | buildSettings = { 520 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 521 | BUNDLE_LOADER = "$(TEST_HOST)"; 522 | CODE_SIGN_STYLE = Automatic; 523 | DEVELOPMENT_TEAM = PA3RF24U47; 524 | INFOPLIST_FILE = "SwiftUI-List-PaginationTests/Info.plist"; 525 | LD_RUNPATH_SEARCH_PATHS = ( 526 | "$(inherited)", 527 | "@executable_path/Frameworks", 528 | "@loader_path/Frameworks", 529 | ); 530 | PRODUCT_BUNDLE_IDENTIFIER = "mcz9mm.SwiftUI-List-PaginationTests"; 531 | PRODUCT_NAME = "$(TARGET_NAME)"; 532 | SWIFT_VERSION = 5.0; 533 | TARGETED_DEVICE_FAMILY = "1,2"; 534 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftUI-List-Pagination.app/SwiftUI-List-Pagination"; 535 | }; 536 | name = Debug; 537 | }; 538 | 3F6933CB22B633F9005D7963 /* Release */ = { 539 | isa = XCBuildConfiguration; 540 | buildSettings = { 541 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 542 | BUNDLE_LOADER = "$(TEST_HOST)"; 543 | CODE_SIGN_STYLE = Automatic; 544 | DEVELOPMENT_TEAM = PA3RF24U47; 545 | INFOPLIST_FILE = "SwiftUI-List-PaginationTests/Info.plist"; 546 | LD_RUNPATH_SEARCH_PATHS = ( 547 | "$(inherited)", 548 | "@executable_path/Frameworks", 549 | "@loader_path/Frameworks", 550 | ); 551 | PRODUCT_BUNDLE_IDENTIFIER = "mcz9mm.SwiftUI-List-PaginationTests"; 552 | PRODUCT_NAME = "$(TARGET_NAME)"; 553 | SWIFT_VERSION = 5.0; 554 | TARGETED_DEVICE_FAMILY = "1,2"; 555 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftUI-List-Pagination.app/SwiftUI-List-Pagination"; 556 | }; 557 | name = Release; 558 | }; 559 | 3F6933CD22B633F9005D7963 /* Debug */ = { 560 | isa = XCBuildConfiguration; 561 | buildSettings = { 562 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 563 | CODE_SIGN_STYLE = Automatic; 564 | DEVELOPMENT_TEAM = PA3RF24U47; 565 | INFOPLIST_FILE = "SwiftUI-List-PaginationUITests/Info.plist"; 566 | LD_RUNPATH_SEARCH_PATHS = ( 567 | "$(inherited)", 568 | "@executable_path/Frameworks", 569 | "@loader_path/Frameworks", 570 | ); 571 | PRODUCT_BUNDLE_IDENTIFIER = "mcz9mm.SwiftUI-List-PaginationUITests"; 572 | PRODUCT_NAME = "$(TARGET_NAME)"; 573 | SWIFT_VERSION = 5.0; 574 | TARGETED_DEVICE_FAMILY = "1,2"; 575 | TEST_TARGET_NAME = "SwiftUI-List-Pagination"; 576 | }; 577 | name = Debug; 578 | }; 579 | 3F6933CE22B633F9005D7963 /* Release */ = { 580 | isa = XCBuildConfiguration; 581 | buildSettings = { 582 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 583 | CODE_SIGN_STYLE = Automatic; 584 | DEVELOPMENT_TEAM = PA3RF24U47; 585 | INFOPLIST_FILE = "SwiftUI-List-PaginationUITests/Info.plist"; 586 | LD_RUNPATH_SEARCH_PATHS = ( 587 | "$(inherited)", 588 | "@executable_path/Frameworks", 589 | "@loader_path/Frameworks", 590 | ); 591 | PRODUCT_BUNDLE_IDENTIFIER = "mcz9mm.SwiftUI-List-PaginationUITests"; 592 | PRODUCT_NAME = "$(TARGET_NAME)"; 593 | SWIFT_VERSION = 5.0; 594 | TARGETED_DEVICE_FAMILY = "1,2"; 595 | TEST_TARGET_NAME = "SwiftUI-List-Pagination"; 596 | }; 597 | name = Release; 598 | }; 599 | /* End XCBuildConfiguration section */ 600 | 601 | /* Begin XCConfigurationList section */ 602 | 3F69339722B633F6005D7963 /* Build configuration list for PBXProject "SwiftUI-List-Pagination" */ = { 603 | isa = XCConfigurationList; 604 | buildConfigurations = ( 605 | 3F6933C422B633F9005D7963 /* Debug */, 606 | 3F6933C522B633F9005D7963 /* Release */, 607 | ); 608 | defaultConfigurationIsVisible = 0; 609 | defaultConfigurationName = Release; 610 | }; 611 | 3F6933C622B633F9005D7963 /* Build configuration list for PBXNativeTarget "SwiftUI-List-Pagination" */ = { 612 | isa = XCConfigurationList; 613 | buildConfigurations = ( 614 | 3F6933C722B633F9005D7963 /* Debug */, 615 | 3F6933C822B633F9005D7963 /* Release */, 616 | ); 617 | defaultConfigurationIsVisible = 0; 618 | defaultConfigurationName = Release; 619 | }; 620 | 3F6933C922B633F9005D7963 /* Build configuration list for PBXNativeTarget "SwiftUI-List-PaginationTests" */ = { 621 | isa = XCConfigurationList; 622 | buildConfigurations = ( 623 | 3F6933CA22B633F9005D7963 /* Debug */, 624 | 3F6933CB22B633F9005D7963 /* Release */, 625 | ); 626 | defaultConfigurationIsVisible = 0; 627 | defaultConfigurationName = Release; 628 | }; 629 | 3F6933CC22B633F9005D7963 /* Build configuration list for PBXNativeTarget "SwiftUI-List-PaginationUITests" */ = { 630 | isa = XCConfigurationList; 631 | buildConfigurations = ( 632 | 3F6933CD22B633F9005D7963 /* Debug */, 633 | 3F6933CE22B633F9005D7963 /* Release */, 634 | ); 635 | defaultConfigurationIsVisible = 0; 636 | defaultConfigurationName = Release; 637 | }; 638 | /* End XCConfigurationList section */ 639 | }; 640 | rootObject = 3F69339422B633F6005D7963 /* Project object */; 641 | } 642 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination.xcodeproj/xcuserdata/mataraishinonome.io.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SwiftUI-List-Pagination.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // SwiftUI-List-Pagination 4 | // 5 | // Created by MataraiKaoru on 2019/06/16. 6 | // Copyright © 2019 MataraiKaoru. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | 21 | func applicationWillTerminate(_ application: UIApplication) { 22 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 23 | } 24 | 25 | // MARK: UISceneSession Lifecycle 26 | 27 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 28 | // Called when a new scene session is being created. 29 | // Use this method to select a configuration to create the new scene with. 30 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 31 | } 32 | 33 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 34 | // Called when the user discards a scene session. 35 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 36 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 37 | } 38 | 39 | 40 | } 41 | 42 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /SwiftUI-List-Pagination/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SwiftUI-List-Pagination/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // SwiftUI-List-Pagination 4 | // 5 | // Created by MataraiKaoru on 2019/06/16. 6 | // Copyright © 2019 MataraiKaoru. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct ContentView : View { 12 | @EnvironmentObject private var viewModel: RowDataViewModel 13 | @State private var pageIndex = 0 14 | private let count = 20 15 | 16 | var body: some View { 17 | 18 | NavigationView { 19 | List { 20 | ForEach(self.viewModel.rowDataModels) { rowData in 21 | if rowData.isEndIndex { 22 | ListRow(model: rowData).onAppear { 23 | if !self.viewModel.isLoading { 24 | self.pageIndex += 1 25 | self.viewModel.fetch(page: self.pageIndex, count: self.count) 26 | print("==========\(self.pageIndex)==========") 27 | } 28 | } 29 | } else { 30 | ListRow(model: rowData) 31 | } 32 | } 33 | LoadingRow(isLoading: true) 34 | } 35 | .navigationBarTitle(Text("Pagination:\(pageIndex)"), displayMode: .large) 36 | } 37 | .onAppear { 38 | guard self.viewModel.rowDataModels.isEmpty else { 39 | return 40 | } 41 | self.viewModel.fetch(page: self.pageIndex, count: self.count) 42 | } 43 | } 44 | } 45 | 46 | #if DEBUG 47 | struct ContentView_Previews : PreviewProvider { 48 | static var previews: some View { 49 | ContentView() 50 | .environmentObject(RowDataViewModel()) 51 | } 52 | } 53 | #endif 54 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination/FetchData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FetchData.swift 3 | // SwiftUI-List-Pagination 4 | // 5 | // Created by MataraiKaoru on 2019/06/16. 6 | // Copyright © 2019 MataraiKaoru. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import Combine 11 | 12 | func fetchData(page: Int, count: Int) -> AnyPublisher<([RowData]), RequestError> { 13 | 14 | AnyPublisher<([RowData]), RequestError> { subscriber in 15 | 16 | print("PAGE: \(page)") 17 | print("COUNT: \(count)") 18 | 19 | // fetch api data... 20 | // sample project is return mock data 21 | var models = [RowData]() 22 | for _ in 1...count { 23 | let model = RowData(id: UUID()) 24 | models.append(model) 25 | } 26 | 27 | if models.isEmpty { 28 | subscriber.receive(completion: .failure(.loadFail)) 29 | } else { 30 | 31 | // In the sample we are delaying purposely 32 | DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { 33 | _ = subscriber.receive(models) 34 | subscriber.receive(completion: .finished) 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UILaunchStoryboardName 33 | LaunchScreen 34 | UISceneConfigurationName 35 | Default Configuration 36 | UISceneDelegateClassName 37 | $(PRODUCT_MODULE_NAME).SceneDelegate 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIRequiredDeviceCapabilities 45 | 46 | armv7 47 | 48 | UISupportedInterfaceOrientations 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | UISupportedInterfaceOrientations~ipad 55 | 56 | UIInterfaceOrientationPortrait 57 | UIInterfaceOrientationPortraitUpsideDown 58 | UIInterfaceOrientationLandscapeLeft 59 | UIInterfaceOrientationLandscapeRight 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination/ListRow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListRow.swift 3 | // SwiftUI-List-Pagination 4 | // 5 | // Created by MataraiKaoru on 2019/06/16. 6 | // Copyright © 2019 MataraiKaoru. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct ListRow : View { 12 | var model: RowData 13 | 14 | var body: some View { 15 | HStack { 16 | Spacer() 17 | 18 | if !model.isEndIndex { 19 | Text("LIST ROW") 20 | .font(.headline) 21 | } else { 22 | Text("BOTTOM ROW") 23 | .font(.headline) 24 | .color(Color.blue) 25 | } 26 | 27 | Spacer() 28 | } 29 | .frame(height: 60) 30 | } 31 | } 32 | 33 | #if DEBUG 34 | struct ListRow_Previews : PreviewProvider { 35 | static var previews: some View { 36 | Group { 37 | ListRow(model: RowData(id: UUID(), isEndIndex: false)) 38 | ListRow(model: RowData(id: UUID(), isEndIndex: true)) 39 | } 40 | .previewLayout(.fixed(width: 400, height: 60)) 41 | } 42 | } 43 | #endif 44 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination/Loading/LoadingRow.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadingRow.swift 3 | // SwiftUI-List-Pagination 4 | // 5 | // Created by MataraiKaoru on 2019/06/16. 6 | // Copyright © 2019 MataraiKaoru. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct LoadingRow : View { 12 | @State var isLoading: Bool 13 | 14 | var body: some View { 15 | HStack { 16 | Spacer() 17 | LoadingView(isLoading: isLoading) 18 | Spacer() 19 | } 20 | } 21 | } 22 | 23 | #if DEBUG 24 | struct LoadingRow_Previews : PreviewProvider { 25 | static var previews: some View { 26 | LoadingRow(isLoading: true) 27 | .previewLayout(.fixed(width: 400, height: 60)) 28 | } 29 | } 30 | #endif 31 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination/Loading/LoadingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadingView.swift 3 | // SwiftUI-List-Pagination 4 | // 5 | // Created by MataraiKaoru on 2019/06/16. 6 | // Copyright © 2019 MataraiKaoru. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct LoadingView: UIViewRepresentable { 12 | 13 | var isLoading: Bool 14 | 15 | func makeUIView(context: Context) -> UIActivityIndicatorView { 16 | let indicator = UIActivityIndicatorView(frame: .zero) 17 | indicator.style = .large 18 | indicator.hidesWhenStopped = true 19 | return indicator 20 | } 21 | 22 | func updateUIView(_ view: UIActivityIndicatorView, context: Context) { 23 | if self.isLoading { 24 | view.startAnimating() 25 | } else { 26 | view.stopAnimating() 27 | } 28 | } 29 | } 30 | 31 | #if DEBUG 32 | struct LoadingView_Previews : PreviewProvider { 33 | static var previews: some View { 34 | LoadingView(isLoading: true) 35 | } 36 | } 37 | #endif 38 | 39 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination/Model/RowData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RowData.swift 3 | // SwiftUI-List-Pagination 4 | // 5 | // Created by MataraiKaoru on 2019/06/16. 6 | // Copyright © 2019 MataraiKaoru. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | 11 | struct RowData: Identifiable { 12 | var id = UUID() 13 | var isEndIndex = false 14 | } 15 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination/Model/RowDataViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RowDataViewModel.swift 3 | // SwiftUI-List-Pagination 4 | // 5 | // Created by MataraiKaoru on 2019/06/16. 6 | // Copyright © 2019 MataraiKaoru. All rights reserved. 7 | // 8 | 9 | import SwiftUI 10 | import Combine 11 | 12 | final class RowDataViewModel: BindableObject { 13 | 14 | var didChange = PassthroughSubject() 15 | 16 | private(set) var rowDataModels = [RowData]() { 17 | didSet { 18 | didChange.send(self) 19 | } 20 | } 21 | 22 | private(set) var isLoading = false { 23 | didSet { 24 | didChange.send(self) 25 | } 26 | } 27 | 28 | func fetch(page: Int, count: Int) { 29 | 30 | guard !isLoading else { 31 | return 32 | } 33 | 34 | isLoading = true 35 | 36 | fetchData(page: page, count: count) 37 | .map { $0 } 38 | .replaceError(with: []) 39 | .receive(subscriber: Subscribers.Sink> { [weak self] fetchedDataModels in 40 | 41 | guard var models = self?.rowDataModels else { return } 42 | models.append(contentsOf: fetchedDataModels) 43 | self?.appendEndlInfo(to: models) 44 | }) 45 | } 46 | 47 | func appendEndlInfo(to models: [RowData]) { 48 | 49 | var tmpModels = models.map { 50 | RowData(id: $0.id) 51 | } 52 | 53 | guard var lastModel = tmpModels.last else { 54 | return 55 | } 56 | 57 | lastModel.isEndIndex = true 58 | tmpModels[models.count - 1] = lastModel 59 | 60 | isLoading = false 61 | rowDataModels = tmpModels 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SwiftUI-List-Pagination/RequestError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestError.swift 3 | // SwiftUI-List-Pagination 4 | // 5 | // Created by MataraiKaoru on 2019/06/16. 6 | // Copyright © 2019 MataraiKaoru. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum RequestError: Error { 12 | case loadFail 13 | } 14 | -------------------------------------------------------------------------------- /SwiftUI-List-Pagination/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // SwiftUI-List-Pagination 4 | // 5 | // Created by MataraiKaoru on 2019/06/16. 6 | // Copyright © 2019 MataraiKaoru. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftUI 11 | 12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 21 | 22 | // Use a UIHostingController as window root view controller 23 | let window = UIWindow(frame: UIScreen.main.bounds) 24 | window.rootViewController = UIHostingController(rootView: ContentView().environmentObject(RowDataViewModel())) 25 | self.window = window 26 | window.makeKeyAndVisible() 27 | } 28 | 29 | func sceneDidDisconnect(_ scene: UIScene) { 30 | // Called as the scene is being released by the system. 31 | // This occurs shortly after the scene enters the background, or when its session is discarded. 32 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 33 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 34 | } 35 | 36 | func sceneDidBecomeActive(_ scene: UIScene) { 37 | // Called when the scene has moved from an inactive state to an active state. 38 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 39 | } 40 | 41 | func sceneWillResignActive(_ scene: UIScene) { 42 | // Called when the scene will move from an active state to an inactive state. 43 | // This may occur due to temporary interruptions (ex. an incoming phone call). 44 | } 45 | 46 | func sceneWillEnterForeground(_ scene: UIScene) { 47 | // Called as the scene transitions from the background to the foreground. 48 | // Use this method to undo the changes made on entering the background. 49 | } 50 | 51 | func sceneDidEnterBackground(_ scene: UIScene) { 52 | // Called as the scene transitions from the foreground to the background. 53 | // Use this method to save data, release shared resources, and store enough scene-specific state information 54 | // to restore the scene back to its current state. 55 | } 56 | 57 | 58 | } 59 | 60 | -------------------------------------------------------------------------------- /SwiftUI-List-PaginationTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SwiftUI-List-PaginationTests/SwiftUI_List_PaginationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUI_List_PaginationTests.swift 3 | // SwiftUI-List-PaginationTests 4 | // 5 | // Created by MataraiKaoru on 2019/06/16. 6 | // Copyright © 2019 MataraiKaoru. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftUI_List_Pagination 11 | 12 | class SwiftUI_List_PaginationTests: XCTestCase { 13 | 14 | override func setUp() { 15 | // Put setup code here. This method is called before the invocation of each test method in the class. 16 | } 17 | 18 | override func tearDown() { 19 | // Put teardown code here. This method is called after the invocation of each test method in the class. 20 | } 21 | 22 | func testExample() { 23 | // This is an example of a functional test case. 24 | // Use XCTAssert and related functions to verify your tests produce the correct results. 25 | } 26 | 27 | func testPerformanceExample() { 28 | // This is an example of a performance test case. 29 | self.measure { 30 | // Put the code you want to measure the time of here. 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /SwiftUI-List-PaginationUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SwiftUI-List-PaginationUITests/SwiftUI_List_PaginationUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftUI_List_PaginationUITests.swift 3 | // SwiftUI-List-PaginationUITests 4 | // 5 | // Created by MataraiKaoru on 2019/06/16. 6 | // Copyright © 2019 MataraiKaoru. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class SwiftUI_List_PaginationUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | 16 | // In UI tests it is usually best to stop immediately when a failure occurs. 17 | continueAfterFailure = false 18 | 19 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 20 | XCUIApplication().launch() 21 | 22 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 23 | } 24 | 25 | override func tearDown() { 26 | // Put teardown code here. This method is called after the invocation of each test method in the class. 27 | } 28 | 29 | func testExample() { 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | } 35 | --------------------------------------------------------------------------------