├── .gitignore
├── FastImageCache
├── FastImageCache.xcodeproj
│ ├── project.pbxproj
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── FastImageCache.xcscheme
├── FastImageCache
│ ├── FastImageCache.h
│ ├── FastImageCache
│ │ ├── FICEntity.h
│ │ ├── FICImageCache+FICErrorLogging.h
│ │ ├── FICImageCache.h
│ │ ├── FICImageCache.m
│ │ ├── FICImageFormat.h
│ │ ├── FICImageFormat.m
│ │ ├── FICImageTable.h
│ │ ├── FICImageTable.m
│ │ ├── FICImageTableChunk.h
│ │ ├── FICImageTableChunk.m
│ │ ├── FICImageTableEntry.h
│ │ ├── FICImageTableEntry.m
│ │ ├── FICImports.h
│ │ ├── FICUtilities.h
│ │ └── FICUtilities.m
│ └── Info.plist
├── FastImageCacheDemo
│ ├── Assets.xcassets
│ │ ├── Icon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── iPad-App-iOS7.png
│ │ │ ├── iPad-App-iOS7@2x.png
│ │ │ ├── iPad-App.png
│ │ │ ├── iPad-App@2x.png
│ │ │ ├── iPhone-App-iOS7@2x.png
│ │ │ ├── iPhone-App.png
│ │ │ └── iPhone-App@2x.png
│ │ └── Launch Image.launchimage
│ │ │ ├── Contents.json
│ │ │ ├── iPad-Portrait-1004h.png
│ │ │ ├── iPad-Portrait-1004h@2x.png
│ │ │ ├── iPad-Portrait-iOS7.png
│ │ │ ├── iPad-Portrait-iOS7@2x.png
│ │ │ ├── iPhone-Portrait-R4@2x-1.png
│ │ │ ├── iPhone-Portrait-R4@2x.png
│ │ │ ├── iPhone-Portrait-iOS7@2x.png
│ │ │ ├── iPhone-Portrait.png
│ │ │ └── iPhone-Portrait@2x.png
│ ├── Classes
│ │ ├── FICDAppDelegate.h
│ │ ├── FICDAppDelegate.m
│ │ ├── FICDFullscreenPhotoDisplayController.h
│ │ ├── FICDFullscreenPhotoDisplayController.m
│ │ ├── FICDPhoto.h
│ │ ├── FICDPhoto.m
│ │ ├── FICDPhotosTableViewCell.h
│ │ ├── FICDPhotosTableViewCell.m
│ │ ├── FICDTableView.h
│ │ ├── FICDTableView.m
│ │ ├── FICDViewController.h
│ │ └── FICDViewController.m
│ ├── Demo Images
│ │ └── README
│ ├── FastImageCacheDemo-Prefix.pch
│ ├── Info.plist
│ ├── fetch_demo_images.sh
│ └── main.m
└── FastImageCacheTests
│ ├── FastImageCacheTests.m
│ └── Info.plist
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | # Mac OS X Finder
2 | .DS_Store
3 |
4 | # Xcode
5 | build/*
6 | *.pbxuser
7 | !default.pbxuser
8 | *.mode1v3
9 | !default.mode1v3
10 | *.mode2v3
11 | !default.mode2v3
12 | *.perspectivev3
13 | !default.perspectivev3
14 | *.xcworkspace
15 | !default.xcworkspace
16 | xcuserdata
17 | profile
18 | *.moved-aside
19 | DerivedData
20 |
21 | # Demo Images
22 | FastImageCache/FastImageCacheDemo/Demo Images/*.jpg
23 | Carthage
24 |
--------------------------------------------------------------------------------
/FastImageCache/FastImageCache.xcodeproj/xcshareddata/xcschemes/FastImageCache.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
67 |
68 |
78 |
79 |
85 |
86 |
87 |
88 |
89 |
90 |
96 |
97 |
103 |
104 |
105 |
106 |
108 |
109 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/FastImageCache/FastImageCache/FastImageCache.h:
--------------------------------------------------------------------------------
1 | //
2 | // FastImageCache.h
3 | // FastImageCache
4 | //
5 | // Created by Rui Peres on 17/06/2015.
6 | // Copyright (c) 2015 Path. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for FastImageCache.
12 | FOUNDATION_EXPORT double FastImageCacheVersionNumber;
13 |
14 | //! Project version string for FastImageCache.
15 | FOUNDATION_EXPORT const unsigned char FastImageCacheVersionString[];
16 |
17 | #import
18 | #import
19 | #import
--------------------------------------------------------------------------------
/FastImageCache/FastImageCache/FastImageCache/FICEntity.h:
--------------------------------------------------------------------------------
1 | //
2 | // FICEntity.h
3 | // FastImageCache
4 | //
5 | // Copyright (c) 2013 Path, Inc.
6 | // See LICENSE for full license agreement.
7 | //
8 |
9 | #import "FICImports.h"
10 | @class FICImageFormat;
11 |
12 | NS_ASSUME_NONNULL_BEGIN
13 |
14 | typedef void (^FICEntityImageDrawingBlock)(CGContextRef context, CGSize contextSize);
15 |
16 | /**
17 | `FICEntity` is a protocol that classes interacting with the image cache must conform to. An entity uniquely identifies entries in image tables, which are instances of ``.
18 | */
19 | @protocol FICEntity
20 |
21 | @required
22 |
23 | /**
24 | A string that uniquely identifies this entity.
25 |
26 | @discussion Within each image table, each entry is identified by an entity's UUID. Ideally, this value should never change for an entity. For example, if your entity class is a person
27 | model, its UUID might be an API-assigned, unchanging, unique user ID. No matter how the properties of the person change, its user ID should never change.
28 | */
29 | @property (nonatomic, copy, readonly) NSString *fic_UUID;
30 |
31 | /**
32 | A string that uniquely identifies an entity's source image.
33 |
34 | @discussion While `` should be unchanging, a source image UUID might change. For example, if your entity class is a person model, its source image UUID might change every time the
35 | person changes their profile photo. In this case, the source image UUID might be a hash of the profile photo URL (assuming each image is given a unique URL).
36 | */
37 | @property (nonatomic, copy, readonly) NSString *fic_sourceImageUUID;
38 |
39 | /**
40 | Returns the source image URL associated with a specific format name.
41 |
42 | @param formatName The name of the image format that identifies which image table is requesting the source image.
43 |
44 | @return A URL representing the requested source image.
45 |
46 | @discussion Fast Image Cache operates on URLs when requesting source images. Typically, these URLs will point to remote image resources that must be downloaded from the Internet. While the
47 | URL returned by this method must be a valid instance of `NSURL`, it does not need to point to an actual remote resource. The URL might point to a file path on disk or be composed of a custom
48 | URL scheme of your choosing. The image cache's delegate is prompted to provide a source image for a particular entity and format name when it cannot find the requested image. It only uses the
49 | URL returned by this method to key image cache requests. No network or file operations are performed by the image cache.
50 |
51 | An example of when this method might return different source image URLs for the same entity is if you have defined several image formats for different thumbnail sizes and styles. For very
52 | large thumbnails, the source image URL might be the original image. For smaller thumbnails, the source image URL might point to a downscaled version of the original image.
53 |
54 | @see FICImageFormat
55 | @see [FICImageCacheDelegate imageCache:wantsSourceImageForEntity:withFormatName:completionBlock:]
56 | */
57 | - (nullable NSURL *)fic_sourceImageURLWithFormatName:(NSString *)formatName;
58 |
59 |
60 | /**
61 | Returns the drawing block for a specific image and format name.
62 |
63 | @param image The cached image that represents this entity.
64 |
65 | @param formatName The name of the image format that identifies which image table is requesting the source image.
66 |
67 | @return The drawing block used to draw the image data to be stored in the image table.
68 |
69 | The drawing block's type is defined as follows:
70 |
71 | typedef void (^FICEntityImageDrawingBlock)(CGContextRef context, CGSize contextSize)
72 |
73 | @discussion Each entity is responsible for drawing its own source image into the bitmap context provided by the image table that will store the image data. Often it is sufficient to simply
74 | draw the image into the bitmap context. However, if you wish to apply any additional graphics processing to the source image before it is stored (such as clipping the image to a roundect rect),
75 | you may use this block to do so.
76 |
77 | @note This block will always be called from the serial dispatch queue used by the image cache.
78 | */
79 | - (nullable FICEntityImageDrawingBlock)fic_drawingBlockForImage:(UIImage *)image withFormatName:(NSString *)formatName;
80 |
81 | @optional
82 | /**
83 | Returns the image for a format
84 |
85 | @param format The image format that identifies which image table is requesting the source image.
86 | */
87 | - (nullable UIImage *)fic_imageForFormat:(FICImageFormat *)format;
88 |
89 | @end
90 |
91 | NS_ASSUME_NONNULL_END
--------------------------------------------------------------------------------
/FastImageCache/FastImageCache/FastImageCache/FICImageCache+FICErrorLogging.h:
--------------------------------------------------------------------------------
1 | //
2 | // FICImageCache+FICErrorLogging.h
3 | // FastImageCache
4 | //
5 | // Copyright (c) 2013 Path, Inc.
6 | // See LICENSE for full license agreement.
7 | //
8 |
9 | #import "FICImageCache.h"
10 |
11 | /**
12 | This category on `` simply exposes its private logging mechanism to other classes.
13 | */
14 | @interface FICImageCache (FICErrorLogging)
15 |
16 | ///-----------------------------
17 | /// @name Logging Error Messages
18 | ///-----------------------------
19 |
20 | /**
21 | Passes an error message to the image cache.
22 |
23 | @param message A string representing the error message.
24 |
25 | @discussion Rather than logging directly to standard output, Fast Image Cache classes pass all error logging to the shared `` instance. `` then allows its delegate to handle the
26 | message.
27 |
28 | @see [FICImageCacheDelegate imageCache:errorDidOccurWithMessage:]
29 | */
30 | - (void)_logMessage:(NSString *)message;
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/FastImageCache/FastImageCache/FastImageCache/FICImageCache.h:
--------------------------------------------------------------------------------
1 | //
2 | // FICImageCache.h
3 | // FastImageCache
4 | //
5 | // Copyright (c) 2013 Path, Inc.
6 | // See LICENSE for full license agreement.
7 | //
8 |
9 | #import "FICImports.h"
10 | #import "FICImageFormat.h"
11 | #import "FICEntity.h"
12 |
13 | @protocol FICEntity;
14 | @protocol FICImageCacheDelegate;
15 |
16 | typedef void (^FICImageCacheCompletionBlock)(id _Nullable entity, NSString * _Nonnull formatName, UIImage * _Nullable image);
17 | typedef void (^FICImageRequestCompletionBlock)(UIImage * _Nullable sourceImage);
18 |
19 | NS_ASSUME_NONNULL_BEGIN
20 |
21 | /**
22 | `FICImageCache` is the primary class for managing and interacting with the image cache. Applications using the image cache create one or more ``
23 | objects. These formats effectively act as logical groupings for image data stored in the image cache. An `` object is created for each format defined by
24 | your application to allow for efficient storage and retrieval of image data. Image data is keyed off of objects conforming to the `` protocol as well as an
25 | image format name.
26 | */
27 | @interface FICImageCache : NSObject
28 |
29 | /**
30 | The namespace of the image cache.
31 |
32 | @discussion Namespace is responsible for isolation of dirrerent image cache instances on file system level. Namespace should be unique across application.
33 | */
34 |
35 | @property (readonly, nonatomic) NSString *nameSpace;
36 |
37 | ///----------------------------
38 | /// @name Managing the Delegate
39 | ///----------------------------
40 |
41 | /**
42 | The delegate of the image cache.
43 |
44 | @discussion The delegate is responsible for asynchronously providing the source image for an entity. Optionally, the delegate can require that all formats in a format
45 | family for a particular entity be processed. Any errors that occur in the image cache are also communicated back to the delegate.
46 | */
47 | @property (nonatomic, weak) id delegate;
48 |
49 | ///---------------------------------------
50 | /// @name Creating Image Cache instances
51 | ///---------------------------------------
52 |
53 | /**
54 | Returns new image cache.
55 |
56 | @return A new instance of `FICImageCache`.
57 |
58 | @param nameSpace The namespace that uniquely identifies current image cahce entity. If no nameSpace given, default namespace will be used.
59 |
60 | @note Fast Image Cache can either be used as a singleton for convenience or can exist as multiple instances.
61 | However, all instances of `FICImageCache` will make use same dispatch queue. To separate location on disk for storing image tables namespaces are used.
62 |
63 | @see [FICImageCache dispatchQueue]
64 | */
65 |
66 | - (instancetype)initWithNameSpace:(NSString *)nameSpace;
67 |
68 | ///---------------------------------------
69 | /// @name Accessing the Shared Image Cache
70 | ///---------------------------------------
71 |
72 | /**
73 | Returns the shared image cache.
74 |
75 | @return A shared instance of `FICImageCache`.
76 |
77 | @note Shared instance always binded to default namespace.
78 |
79 | @see [FICImageCache dispatchQueue]
80 | */
81 | + (instancetype)sharedImageCache;
82 |
83 | /**
84 | Returns the shared dispatch queue used by all instances of `FICImageCache`.
85 |
86 | @return A generic, shared dispatch queue of type `dispatch_queue_t`.
87 |
88 | @note All instances of `FICImageCache` make use a single, shared dispatch queue to do their work.
89 | */
90 | + (dispatch_queue_t)dispatchQueue;
91 |
92 | ///---------------------------------
93 | /// @name Working with Image Formats
94 | ///---------------------------------
95 |
96 | /**
97 | Sets the image formats to be used by the image cache.
98 |
99 | @param formats An array of `` objects.
100 |
101 | @note Once the image formats have been set, subsequent calls to this method will do nothing.
102 | */
103 | - (void)setFormats:(NSArray *)formats;
104 |
105 | /**
106 | Returns an image format previously associated with the image cache.
107 |
108 | @param formatName The name of the image format to return.
109 |
110 | @return An image format with the name `formatName` or `nil` if no format with that name exists.
111 | */
112 | - (nullable FICImageFormat *)formatWithName:(NSString *)formatName;
113 |
114 | /**
115 | Returns all the image formats of the same family previously associated with the image cache.
116 |
117 | @param family The name of the family of image formats to return.
118 |
119 | @return An array of `` objects whose family is `family` or `nil` if no format belongs to that family.
120 | */
121 | - (nullable NSArray *)formatsWithFamily:(NSString *)family;
122 |
123 | ///-----------------------------------------------
124 | /// @name Storing, Retrieving, and Deleting Images
125 | ///-----------------------------------------------
126 |
127 | /**
128 | Manually sets the the image to be used by the image cache for a particular entity and format name.
129 |
130 | @discussion Usually the image cache's delegate is responsible for lazily providing the source image for a given entity. This source image is then processed according
131 | to the drawing block defined by an entity for a given image format. This method allows the sender to explicitly set the image data to be stored in the image cache.
132 | After the image has been processed by the image cache, the completion block is called asynchronously on the main queue.
133 |
134 | @param image The image to store in the image cache.
135 |
136 | @param entity The entity that uniquely identifies the source image.
137 |
138 | @param formatName The format name that uniquely identifies which image table to look in for the cached image.
139 |
140 | @param completionBlock The completion block that is called after the image has been processed or if an error occurs.
141 |
142 | The completion block's type is defined as follows:
143 |
144 | typedef void (^FICImageCacheCompletionBlock)(id entity, NSString *formatName, UIImage *image)
145 | */
146 | - (void)setImage:(UIImage *)image forEntity:(id )entity withFormatName:(NSString *)formatName completionBlock:(nullable FICImageCacheCompletionBlock)completionBlock;
147 |
148 | /**
149 | Attempts to synchronously retrieve an image from the image cache.
150 |
151 | @param entity The entity that uniquely identifies the source image.
152 |
153 | @param formatName The format name that uniquely identifies which image table to look in for the cached image. Must not be nil.
154 |
155 | @param completionBlock The completion block that is called when the requested image is available or if an error occurs.
156 |
157 | The completion block's type is defined as follows:
158 |
159 | typedef void (^FICImageCacheCompletionBlock)(id entity, NSString *formatName, UIImage *image)
160 |
161 | If the requested image already exists in the image cache, then the completion block is immediately called synchronously on the current thread. If the requested image
162 | does not already exist in the image cache, then the completion block will be called asynchronously on the main thread as soon as the requested image is available.
163 |
164 | @return `YES` if the requested image already exists in the image case, `NO` if the image needs to be provided to the image cache by its delegate.
165 |
166 | @discussion Even if you make a synchronous image retrieval request, if the image does not yet exist in the image cache, the delegate will be asked to provide a source
167 | image, and it will be processed. This always occurs asynchronously. In this case, the return value from this method will be `NO`, and the image will be available in the
168 | completion block.
169 |
170 | @note You can always rely on the completion block being called. If an error occurs for any reason, the `image` parameter of the completion block will be `nil`. See
171 | <[FICImageCacheDelegate imageCache:errorDidOccurWithMessage:]> for information about being notified when errors occur.
172 | */
173 | - (BOOL)retrieveImageForEntity:(id )entity withFormatName:(NSString *)formatName completionBlock:(nullable FICImageCacheCompletionBlock)completionBlock;
174 |
175 | /**
176 | Asynchronously retrieves an image from the image cache.
177 |
178 | @param entity The entity that uniquely identifies the source image.
179 |
180 | @param formatName The format name that uniquely identifies which image table to look in for the cached image. Must not be nil.
181 |
182 | @param completionBlock The completion block that is called when the requested image is available or if an error occurs.
183 |
184 | The completion block's type is defined as follows:
185 |
186 | typedef void (^FICImageCacheCompletionBlock)(id entity, NSString *formatName, UIImage *image)
187 |
188 | Unlike its synchronous counterpart, this method will always call its completion block asynchronously on the main thread, even if the request image is already in the
189 | image cache.
190 |
191 | @return `YES` if the requested image already exists in the image case, `NO` if the image needs to be provided to the image cache by its delegate.
192 |
193 | @note You can always rely on the completion block being called. If an error occurs for any reason, the `image` parameter of the completion block will be `nil`. See
194 | <[FICImageCacheDelegate imageCache:errorDidOccurWithMessage:]> for information about being notified when errors occur.
195 |
196 | @see [FICImageCache retrieveImageForEntity:withFormatName:completionBlock:]
197 | */
198 | - (BOOL)asynchronouslyRetrieveImageForEntity:(id )entity withFormatName:(NSString *)formatName completionBlock:(nullable FICImageCacheCompletionBlock)completionBlock;
199 |
200 | /**
201 | Deletes an image from the image cache.
202 |
203 | @param entity The entity that uniquely identifies the source image.
204 |
205 | @param formatName The format name that uniquely identifies which image table to look in for the cached image.
206 | */
207 | - (void)deleteImageForEntity:(id )entity withFormatName:(NSString *)formatName;
208 |
209 | ///-------------------------------
210 | /// @name Canceling Image Requests
211 | ///-------------------------------
212 |
213 | /**
214 | Cancels an active request for an image from the image cache.
215 |
216 | @param entity The entity that uniquely identifies the source image.
217 |
218 | @param formatName The format name that uniquely identifies which image table to look in for the cached image.
219 |
220 | @discussion After this method is called, the completion block of the <[FICImageCacheDelegate imageCache:wantsSourceImageForEntity:withFormatName:completionBlock:]> delegate
221 | method for the corresponding entity, if called, does nothing.
222 | */
223 | - (void)cancelImageRetrievalForEntity:(id )entity withFormatName:(NSString *)formatName;
224 |
225 | ///-----------------------------------
226 | /// @name Checking for Image Existence
227 | ///-----------------------------------
228 |
229 | /**
230 | Returns whether or not an image exists in the image cache.
231 |
232 | @param entity The entity that uniquely identifies the source image.
233 |
234 | @param formatName The format name that uniquely identifies which image table to look in for the cached image.
235 |
236 | @return `YES` if an image exists in the image cache for a given entity and format name. Otherwise, `NO`.
237 | */
238 | - (BOOL)imageExistsForEntity:(id )entity withFormatName:(NSString *)formatName;
239 |
240 | ///--------------------------------
241 | /// @name Resetting the Image Cache
242 | ///--------------------------------
243 |
244 | /**
245 | Resets the image cache by deleting all image tables and their contents.
246 |
247 | @note Resetting an image cache does not reset its image formats.
248 | */
249 | - (void)reset;
250 |
251 | @end
252 |
253 | /**
254 | `FICImageCacheDelegate` defines the required and optional actions that an image cache's delegate can perform.
255 | */
256 | @protocol FICImageCacheDelegate
257 |
258 | @optional
259 |
260 | /**
261 | This method is called on the delegate when the image cache needs a source image.
262 |
263 | @param imageCache The image cache that is requesting the source image.
264 |
265 | @param entity The entity that uniquely identifies the source image.
266 |
267 | @param formatName The format name that uniquely identifies which image table to look in for the cached image.
268 |
269 | @param completionBlock The completion block that the receiver must call when it has a source image ready.
270 |
271 | The completion block's type is defined as follows:
272 |
273 | typedef void (^FICImageRequestCompletionBlock)(UIImage *sourceImage)
274 |
275 | The completion block must always be called on the main thread.
276 |
277 | @discussion A source image is usually the original, full-size image that represents an entity. This source image is processed for every unique format to create the
278 | actual image data to be stored in the image cache. This method is an asynchronous data provider, so nothing is actually returned to the sender. Instead, the delegate's
279 | implementation is expected to call the completion block once an image is available.
280 |
281 | Fast Image Cache is architected under the typical design pattern whereby model objects provide a URL to certain image assets and allow the client to actually retrieve
282 | the images via network requests only when needed. As a result, the implementation of this method will usually involve creating an asynchronous network request using
283 | the URL returned by <[FICEntity sourceImageURLWithFormatName:]>, deserializing the image data when the request completes, and finally calling this method's completion
284 | block to provide the image cache with the source image.
285 | */
286 | - (void)imageCache:(FICImageCache *)imageCache wantsSourceImageForEntity:(id )entity withFormatName:(NSString *)formatName completionBlock:(nullable FICImageRequestCompletionBlock)completionBlock;
287 |
288 | /**
289 | This method is called on the delegate when the image cache has received an image retrieval cancellation request.
290 |
291 | @param imageCache The image cache that has received the image retrieval cancellation request.
292 |
293 | @param entity The entity that uniquely identifies the source image.
294 |
295 | @param formatName The format name that uniquely identifies which image table to look in for the cached image.
296 |
297 | @discussion When an image retrieval cancellation request is made to the image cache, it removes all of its internal bookkeeping for requests. However, it is still the
298 | delegate's responsibility to cancel whatever logic is it performing to provide a source image to the cache (e.g., a network request).
299 |
300 | @see [FICImageCache cancelImageRetrievalForEntity:withFormatName:]
301 | */
302 | - (void)imageCache:(FICImageCache *)imageCache cancelImageLoadingForEntity:(id )entity withFormatName:(NSString *)formatName;
303 |
304 | /**
305 | This method is called on the delegate to determine whether or not all formats in a family should be processed right now.
306 |
307 | @note If this method is not implemented by the delegate, the default value is `YES`.
308 |
309 | @param imageCache The image cache that is requesting the source image.
310 |
311 | @param formatFamily The name of a format family.
312 |
313 | @param entity The entity that uniquely identifies the source image.
314 |
315 | @return `YES` if all formats in a format family should be processed. Otherwise, `NO`.
316 |
317 | @discussion This method is called whenever new image data is stored in the image cache. Because format families are used to group multiple different formats together,
318 | typically the delegate will want to return `YES` here so that other formats in the same family can be processed.
319 |
320 | For example, if your image cache has defined several different thumbnail sizes and styles for a person model, and if a person changes their profile photo, you would
321 | want every thumbnail size and style is updated with the new source image.
322 | */
323 | - (BOOL)imageCache:(FICImageCache *)imageCache shouldProcessAllFormatsInFamily:(NSString *)formatFamily forEntity:(id )entity;
324 |
325 | /**
326 | This method is called on the delegate whenever the image cache has an error message to log.
327 |
328 | @param imageCache The image cache that is requesting the source image.
329 |
330 | @param errorMessage The error message generated by the image cache.
331 |
332 | @discussion Fast Image Cache will not explicitly log any messages to standard output. Instead, it allows the delegate to handle (or ignore) any error output.
333 | */
334 | - (void)imageCache:(FICImageCache *)imageCache errorDidOccurWithMessage:(NSString *)errorMessage;
335 |
336 | @end
337 |
338 | NS_ASSUME_NONNULL_END
--------------------------------------------------------------------------------
/FastImageCache/FastImageCache/FastImageCache/FICImageCache.m:
--------------------------------------------------------------------------------
1 | //
2 | // FICImageCache.m
3 | // FastImageCache
4 | //
5 | // Copyright (c) 2013 Path, Inc.
6 | // See LICENSE for full license agreement.
7 | //
8 |
9 | #import "FICImageCache.h"
10 | #import "FICEntity.h"
11 | #import "FICImageTable.h"
12 | #import "FICImageFormat.h"
13 |
14 | #pragma mark Internal Definitions
15 |
16 | static void _FICAddCompletionBlockForEntity(NSString *formatName, NSMutableDictionary *entityRequestsDictionary, id entity, FICImageCacheCompletionBlock completionBlock);
17 |
18 | static NSString *const FICImageCacheFormatKey = @"FICImageCacheFormatKey";
19 | static NSString *const FICImageCacheCompletionBlocksKey = @"FICImageCacheCompletionBlocksKey";
20 | static NSString *const FICImageCacheEntityKey = @"FICImageCacheEntityKey";
21 |
22 | #pragma mark - Class Extension
23 |
24 | @interface FICImageCache () {
25 | NSMutableDictionary *_formats;
26 | NSMutableDictionary *_imageTables;
27 | NSMutableDictionary *_requests;
28 |
29 | BOOL _delegateImplementsWantsSourceImageForEntityWithFormatNameCompletionBlock;
30 | BOOL _delegateImplementsShouldProcessAllFormatsInFamilyForEntity;
31 | BOOL _delegateImplementsErrorDidOccurWithMessage;
32 | BOOL _delegateImplementsCancelImageLoadingForEntityWithFormatName;
33 | }
34 |
35 | @end
36 |
37 | #pragma mark
38 |
39 | @implementation FICImageCache
40 |
41 | @synthesize delegate = _delegate;
42 |
43 | #pragma mark - Property Accessors
44 |
45 | - (void)setDelegate:(id)delegate {
46 | if (delegate != _delegate) {
47 | _delegate = delegate;
48 |
49 | _delegateImplementsWantsSourceImageForEntityWithFormatNameCompletionBlock = [_delegate respondsToSelector:@selector(imageCache:wantsSourceImageForEntity:withFormatName:completionBlock:)];
50 | _delegateImplementsShouldProcessAllFormatsInFamilyForEntity = [_delegate respondsToSelector:@selector(imageCache:shouldProcessAllFormatsInFamily:forEntity:)];
51 | _delegateImplementsErrorDidOccurWithMessage = [_delegate respondsToSelector:@selector(imageCache:errorDidOccurWithMessage:)];
52 | _delegateImplementsCancelImageLoadingForEntityWithFormatName = [_delegate respondsToSelector:@selector(imageCache:cancelImageLoadingForEntity:withFormatName:)];
53 | }
54 | }
55 |
56 | #pragma mark - Object Lifecycle
57 |
58 | + (instancetype)sharedImageCache {
59 | static dispatch_once_t onceToken;
60 | static FICImageCache *__imageCache = nil;
61 | dispatch_once(&onceToken, ^{
62 | __imageCache = [[[self class] alloc] init];
63 | });
64 |
65 | return __imageCache;
66 | }
67 |
68 | + (dispatch_queue_t)dispatchQueue {
69 | static dispatch_queue_t __imageCacheDispatchQueue = NULL;
70 | static dispatch_once_t onceToken;
71 | dispatch_once(&onceToken, ^{
72 | __imageCacheDispatchQueue = dispatch_queue_create("com.path.FastImageCacheQueue", NULL);
73 | });
74 | return __imageCacheDispatchQueue;
75 | }
76 |
77 | - (instancetype)init {
78 | return [self initWithNameSpace:@"FICDefaultNamespace"];
79 | }
80 |
81 | - (instancetype)initWithNameSpace:(NSString *)nameSpace {
82 | self = [super init];
83 | if (self) {
84 | _formats = [[NSMutableDictionary alloc] init];
85 | _imageTables = [[NSMutableDictionary alloc] init];
86 | _requests = [[NSMutableDictionary alloc] init];
87 | _nameSpace = nameSpace;
88 | }
89 | return self;
90 | }
91 |
92 | #pragma mark - Working with Formats
93 |
94 | - (void)setFormats:(NSArray *)formats {
95 | if ([_formats count] > 0) {
96 | [self _logMessage:[NSString stringWithFormat:@"*** FIC Error: %s FICImageCache has already been configured with its image formats.", __PRETTY_FUNCTION__]];
97 | } else {
98 | NSMutableSet *imageTableFiles = [NSMutableSet set];
99 | FICImageFormatDevices currentDevice = [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad ? FICImageFormatDevicePad : FICImageFormatDevicePhone;
100 | for (FICImageFormat *imageFormat in formats) {
101 | NSString *formatName = [imageFormat name];
102 | FICImageFormatDevices devices = [imageFormat devices];
103 | if (devices & currentDevice) {
104 | // Only initialize an image table for this format if it is needed on the current device.
105 | FICImageTable *imageTable = [[FICImageTable alloc] initWithFormat:imageFormat imageCache:self];
106 | [_imageTables setObject:imageTable forKey:formatName];
107 | [_formats setObject:imageFormat forKey:formatName];
108 |
109 | [imageTableFiles addObject:[[imageTable tableFilePath] lastPathComponent]];
110 | [imageTableFiles addObject:[[imageTable metadataFilePath] lastPathComponent]];
111 | }
112 | }
113 |
114 | // Remove any extraneous files in the image tables directory
115 | NSFileManager *fileManager = [NSFileManager defaultManager];
116 | NSString *directoryPath = [FICImageTable directoryPath];
117 | if (self.nameSpace) {
118 | directoryPath = [directoryPath stringByAppendingPathComponent:self.nameSpace];
119 | }
120 | NSArray *fileNames = [fileManager contentsOfDirectoryAtPath:directoryPath error:nil];
121 | for (NSString *fileName in fileNames) {
122 | if ([imageTableFiles containsObject:fileName] == NO) {
123 | // This is an extraneous file, which is no longer needed.
124 | NSString* filePath = [directoryPath stringByAppendingPathComponent:fileName];
125 | [fileManager removeItemAtPath:filePath error:nil];
126 | }
127 | }
128 | }
129 | }
130 |
131 | - (FICImageFormat *)formatWithName:(NSString *)formatName {
132 | return [_formats objectForKey:formatName];
133 | }
134 |
135 | - (NSArray *)formatsWithFamily:(NSString *)family {
136 | NSMutableArray *formats = nil;
137 | for (FICImageFormat *format in [_formats allValues]) {
138 | if ([[format family] isEqualToString:family]) {
139 | if (formats == nil) {
140 | formats = [NSMutableArray array];
141 | }
142 |
143 | [formats addObject:format];
144 | }
145 | }
146 |
147 | return [formats copy];
148 | }
149 |
150 | #pragma mark - Retrieving Images
151 |
152 | - (BOOL)retrieveImageForEntity:(id )entity withFormatName:(NSString *)formatName completionBlock:(FICImageCacheCompletionBlock)completionBlock {
153 | return [self _retrieveImageForEntity:entity withFormatName:formatName loadSynchronously:YES completionBlock:completionBlock];
154 | }
155 |
156 | - (BOOL)asynchronouslyRetrieveImageForEntity:(id )entity withFormatName:(NSString *)formatName completionBlock:(FICImageCacheCompletionBlock)completionBlock {
157 | return [self _retrieveImageForEntity:entity withFormatName:formatName loadSynchronously:NO completionBlock:completionBlock];
158 | }
159 |
160 | - (BOOL)_retrieveImageForEntity:(id )entity withFormatName:(NSString *)formatName loadSynchronously:(BOOL)loadSynchronously completionBlock:(FICImageCacheCompletionBlock)completionBlock {
161 | NSParameterAssert(formatName);
162 |
163 | BOOL imageExists = NO;
164 |
165 | FICImageTable *imageTable = [_imageTables objectForKey:formatName];
166 | NSString *entityUUID = [entity fic_UUID];
167 | NSString *sourceImageUUID = [entity fic_sourceImageUUID];
168 |
169 | if (loadSynchronously == NO && [imageTable entryExistsForEntityUUID:entityUUID sourceImageUUID:sourceImageUUID]) {
170 | imageExists = YES;
171 |
172 | dispatch_async([FICImageCache dispatchQueue], ^{
173 | UIImage *image = [imageTable newImageForEntityUUID:entityUUID sourceImageUUID:sourceImageUUID preheatData:YES];
174 |
175 | if (completionBlock != nil) {
176 | dispatch_async(dispatch_get_main_queue(), ^{
177 | completionBlock(entity, formatName, image);
178 | });
179 | }
180 | });
181 | } else {
182 | UIImage *image = [imageTable newImageForEntityUUID:entityUUID sourceImageUUID:sourceImageUUID preheatData:NO];
183 | imageExists = image != nil;
184 |
185 | dispatch_block_t completionBlockCallingBlock = ^{
186 | if (completionBlock != nil) {
187 | if (loadSynchronously) {
188 | completionBlock(entity, formatName, image);
189 | } else {
190 | dispatch_async(dispatch_get_main_queue(), ^{
191 | completionBlock(entity, formatName, image);
192 | });
193 | }
194 | }
195 | };
196 |
197 | if (image == nil) {
198 | // No image for this UUID exists in the image table. We'll need to ask the delegate to retrieve the source asset.
199 | NSURL *sourceImageURL = [entity fic_sourceImageURLWithFormatName:formatName];
200 |
201 | if (sourceImageURL != nil) {
202 | // We check to see if this image is already being fetched.
203 | BOOL needsToFetch = NO;
204 | @synchronized (_requests) {
205 | NSMutableDictionary *requestDictionary = [_requests objectForKey:sourceImageURL];
206 | if (requestDictionary == nil) {
207 | // If we're here, then we aren't currently fetching this image.
208 | requestDictionary = [NSMutableDictionary dictionary];
209 | [_requests setObject:requestDictionary forKey:sourceImageURL];
210 | needsToFetch = YES;
211 | }
212 |
213 | _FICAddCompletionBlockForEntity(formatName, requestDictionary, entity, completionBlock);
214 | }
215 |
216 | if (needsToFetch) {
217 | @autoreleasepool {
218 | UIImage *image;
219 | if ([entity respondsToSelector:@selector(fic_imageForFormat:)]){
220 | FICImageFormat *format = [self formatWithName:formatName];
221 | image = [entity fic_imageForFormat:format];
222 | }
223 |
224 | if (image){
225 | [self _imageDidLoad:image forURL:sourceImageURL];
226 | } else if (_delegateImplementsWantsSourceImageForEntityWithFormatNameCompletionBlock){
227 | [_delegate imageCache:self wantsSourceImageForEntity:entity withFormatName:formatName completionBlock:^(UIImage *sourceImage) {
228 | [self _imageDidLoad:sourceImage forURL:sourceImageURL];
229 | }];
230 | }
231 | }
232 | }
233 | } else {
234 | NSString *message = [NSString stringWithFormat:@"*** FIC Error: %s entity %@ returned a nil source image URL for image format %@.", __PRETTY_FUNCTION__, entity, formatName];
235 | [self _logMessage:message];
236 |
237 | completionBlockCallingBlock();
238 | }
239 | } else {
240 | completionBlockCallingBlock();
241 | }
242 | }
243 |
244 | return imageExists;
245 | }
246 |
247 | - (void)_imageDidLoad:(UIImage *)image forURL:(NSURL *)URL {
248 | NSDictionary *requestDictionary;
249 | @synchronized (_requests) {
250 | requestDictionary = [_requests objectForKey:URL];
251 | [_requests removeObjectForKey:URL];
252 | // Now safe to use requestsDictionary outside the lock, because we've taken ownership from _requests
253 | }
254 |
255 | if (requestDictionary != nil) {
256 | for (NSMutableDictionary *entityDictionary in [requestDictionary allValues]) {
257 | id entity = [entityDictionary objectForKey:FICImageCacheEntityKey];
258 | NSString *formatName = [entityDictionary objectForKey:FICImageCacheFormatKey];
259 | NSDictionary *completionBlocksDictionary = [entityDictionary objectForKey:FICImageCacheCompletionBlocksKey];
260 | if (image != nil){
261 | [self _processImage:image forEntity:entity completionBlocksDictionary:completionBlocksDictionary];
262 | } else {
263 | NSArray *completionBlocks = [completionBlocksDictionary objectForKey:formatName];
264 | if (completionBlocks != nil) {
265 | dispatch_async(dispatch_get_main_queue(), ^{
266 | for (FICImageCacheCompletionBlock completionBlock in completionBlocks) {
267 | completionBlock(entity, formatName, nil);
268 | }
269 | });
270 | }
271 | }
272 | }
273 | }
274 | }
275 |
276 | static void _FICAddCompletionBlockForEntity(NSString *formatName, NSMutableDictionary *entityRequestsDictionary, id entity, FICImageCacheCompletionBlock completionBlock) {
277 | NSString *entityUUID = [entity fic_UUID];
278 | NSMutableDictionary *requestDictionary = [entityRequestsDictionary objectForKey:entityUUID];
279 | NSMutableDictionary *completionBlocks = nil;
280 |
281 | if (requestDictionary == nil) {
282 | // This is the first time we're dealing with this particular entity for this URL request.
283 | requestDictionary = [NSMutableDictionary dictionaryWithObjectsAndKeys:entity, FICImageCacheEntityKey, nil];
284 | [entityRequestsDictionary setObject:requestDictionary forKey:entityUUID];
285 | [requestDictionary setObject:formatName forKey:FICImageCacheFormatKey];
286 |
287 | // Dictionary where keys are imageFormats, and each value is an array of the completion blocks for the requests for this
288 | // URL at the specified format.
289 | completionBlocks = [NSMutableDictionary dictionary];
290 | [requestDictionary setObject:completionBlocks forKey:FICImageCacheCompletionBlocksKey];
291 | } else {
292 | // We already have a request dictionary for this entity, so we just need to append a completion block.
293 | completionBlocks = [requestDictionary objectForKey:FICImageCacheCompletionBlocksKey];
294 | }
295 |
296 | if (completionBlock != nil) {
297 | NSMutableArray *blocksArray = [completionBlocks objectForKey:formatName];
298 | if (blocksArray == nil) {
299 | blocksArray = [NSMutableArray array];
300 | [completionBlocks setObject:blocksArray forKey:formatName];
301 | }
302 |
303 | FICImageCacheCompletionBlock completionBlockCopy = [completionBlock copy];
304 | [blocksArray addObject:completionBlockCopy];
305 | }
306 | }
307 |
308 | #pragma mark - Storing Images
309 |
310 | - (void)setImage:(UIImage *)image forEntity:(id )entity withFormatName:(NSString *)formatName completionBlock:(FICImageCacheCompletionBlock)completionBlock {
311 | if (image != nil && entity != nil) {
312 | NSDictionary *completionBlocksDictionary = nil;
313 |
314 | if (completionBlock != nil) {
315 | completionBlocksDictionary = [NSDictionary dictionaryWithObject:[NSArray arrayWithObject:[completionBlock copy]] forKey:formatName];
316 | }
317 |
318 | NSString *entityUUID = [entity fic_UUID];
319 | FICImageTable *imageTable = [_imageTables objectForKey:formatName];
320 | if (imageTable) {
321 | [imageTable deleteEntryForEntityUUID:entityUUID];
322 |
323 | [self _processImage:image forEntity:entity completionBlocksDictionary:completionBlocksDictionary];
324 | } else {
325 | [self _logMessage:[NSString stringWithFormat:@"*** FIC Error: %s Couldn't find image table with format name %@", __PRETTY_FUNCTION__, formatName]];
326 | }
327 | }
328 | }
329 |
330 | - (void)_processImage:(UIImage *)image forEntity:(id )entity completionBlocksDictionary:(NSDictionary *)completionBlocksDictionary {
331 | for (NSString *formatToProcess in [self formatsToProcessForCompletionBlocks:completionBlocksDictionary
332 | entity:entity]) {
333 | FICImageTable *imageTable = [_imageTables objectForKey:formatToProcess];
334 | NSArray *completionBlocks = [completionBlocksDictionary objectForKey:formatToProcess];
335 | [self _processImage:image forEntity:entity imageTable:imageTable completionBlocks:completionBlocks];
336 | }
337 | }
338 |
339 | - (void)_processImage:(UIImage *)image forEntity:(id )entity imageTable:(FICImageTable *)imageTable completionBlocks:(NSArray *)completionBlocks {
340 | if (imageTable != nil) {
341 | if ([entity fic_UUID] == nil) {
342 | [self _logMessage:[NSString stringWithFormat:@"*** FIC Error: %s entity %@ is missing its UUID.", __PRETTY_FUNCTION__, entity]];
343 | return;
344 | }
345 |
346 | if ([entity fic_sourceImageUUID] == nil) {
347 | [self _logMessage:[NSString stringWithFormat:@"*** FIC Error: %s entity %@ is missing its source image UUID.", __PRETTY_FUNCTION__, entity]];
348 | return;
349 | }
350 |
351 | NSString *entityUUID = [entity fic_UUID];
352 | NSString *sourceImageUUID = [entity fic_sourceImageUUID];
353 | FICImageFormat *imageFormat = [imageTable imageFormat];
354 | NSString *imageFormatName = [imageFormat name];
355 | FICEntityImageDrawingBlock imageDrawingBlock = [entity fic_drawingBlockForImage:image withFormatName:imageFormatName];
356 |
357 | dispatch_async([FICImageCache dispatchQueue], ^{
358 | [imageTable setEntryForEntityUUID:entityUUID sourceImageUUID:sourceImageUUID imageDrawingBlock:imageDrawingBlock];
359 |
360 | UIImage *resultImage = [imageTable newImageForEntityUUID:entityUUID sourceImageUUID:sourceImageUUID preheatData:NO];
361 |
362 | if (completionBlocks != nil) {
363 | dispatch_async(dispatch_get_main_queue(), ^{
364 | NSString *formatName = [[imageTable imageFormat] name];
365 | for (FICImageCacheCompletionBlock completionBlock in completionBlocks) {
366 | completionBlock(entity, formatName, resultImage);
367 | }
368 | });
369 | }
370 | });
371 | }
372 | }
373 |
374 | - (NSSet *)formatsToProcessForCompletionBlocks:(NSDictionary *)completionBlocksDictionary entity:(id )entity {
375 | // At the very least, we must process all formats with pending completion blocks
376 | NSMutableSet *formatsToProcess = [NSMutableSet setWithArray:completionBlocksDictionary.allKeys];
377 |
378 | // Get the list of format families included by the formats we have to process
379 | NSMutableSet *families;
380 | for (NSString *formatToProcess in formatsToProcess) {
381 | FICImageTable *imageTable = _imageTables[formatToProcess];
382 | FICImageFormat *imageFormat = imageTable.imageFormat;
383 | NSString *tableFormatFamily = imageFormat.family;
384 | if (tableFormatFamily) {
385 | if (!families) {
386 | families = [NSMutableSet set];
387 | }
388 | [families addObject:tableFormatFamily];
389 | }
390 | }
391 |
392 | // The delegate can override the list of families to process
393 | if (_delegateImplementsShouldProcessAllFormatsInFamilyForEntity) {
394 | [families minusSet:[families objectsPassingTest:^BOOL(NSString *familyName, BOOL *stop) {
395 | return ![_delegate imageCache:self shouldProcessAllFormatsInFamily:familyName forEntity:entity];
396 | }]];
397 | }
398 |
399 | // Ensure that all formats from all of those families are included in the list
400 | if (families.count) {
401 | for (FICImageTable *table in _imageTables.allValues) {
402 | FICImageFormat *imageFormat = table.imageFormat;
403 | NSString *imageFormatName = imageFormat.name;
404 | // If we're already processing this format, keep looking
405 | if ([formatsToProcess containsObject:imageFormatName]) {
406 | continue;
407 | }
408 |
409 | // If this format isn't included in any referenced family, keep looking
410 | if (![families containsObject:imageFormat.family]) {
411 | continue;
412 | }
413 |
414 | // If the image already exists, keep going
415 | if ([table entryExistsForEntityUUID:entity.fic_UUID sourceImageUUID:entity.fic_sourceImageUUID]) {
416 | continue;
417 | }
418 |
419 | [formatsToProcess addObject:imageFormatName];
420 | }
421 | }
422 |
423 | return formatsToProcess;
424 | }
425 |
426 | #pragma mark - Checking for Image Existence
427 |
428 | - (BOOL)imageExistsForEntity:(id )entity withFormatName:(NSString *)formatName {
429 | FICImageTable *imageTable = [_imageTables objectForKey:formatName];
430 | NSString *entityUUID = [entity fic_UUID];
431 | NSString *sourceImageUUID = [entity fic_sourceImageUUID];
432 |
433 | BOOL imageExists = [imageTable entryExistsForEntityUUID:entityUUID sourceImageUUID:sourceImageUUID];
434 |
435 | return imageExists;
436 | }
437 |
438 | #pragma mark - Invalidating Image Data
439 |
440 | - (void)deleteImageForEntity:(id )entity withFormatName:(NSString *)formatName {
441 | FICImageTable *imageTable = [_imageTables objectForKey:formatName];
442 | NSString *entityUUID = [entity fic_UUID];
443 | [imageTable deleteEntryForEntityUUID:entityUUID];
444 | }
445 |
446 | - (void)cancelImageRetrievalForEntity:(id )entity withFormatName:(NSString *)formatName {
447 | NSURL *sourceImageURL = [entity fic_sourceImageURLWithFormatName:formatName];
448 | NSString *entityUUID = [entity fic_UUID];
449 |
450 | BOOL cancelImageLoadingForEntity = NO;
451 | @synchronized (_requests) {
452 | NSMutableDictionary *requestDictionary = [_requests objectForKey:sourceImageURL];
453 | if (requestDictionary) {
454 | NSMutableDictionary *entityRequestsDictionary = [requestDictionary objectForKey:entityUUID];
455 | if (entityRequestsDictionary) {
456 | NSMutableDictionary *completionBlocksDictionary = [entityRequestsDictionary objectForKey:FICImageCacheCompletionBlocksKey];
457 | [completionBlocksDictionary removeObjectForKey:formatName];
458 |
459 | if ([completionBlocksDictionary count] == 0) {
460 | [requestDictionary removeObjectForKey:entityUUID];
461 | }
462 |
463 | if ([requestDictionary count] == 0) {
464 | [_requests removeObjectForKey:sourceImageURL];
465 | cancelImageLoadingForEntity = YES;
466 | }
467 | }
468 | }
469 | }
470 |
471 | if (cancelImageLoadingForEntity && _delegateImplementsCancelImageLoadingForEntityWithFormatName) {
472 | [_delegate imageCache:self cancelImageLoadingForEntity:entity withFormatName:formatName];
473 | }
474 | }
475 |
476 | - (void)reset {
477 | for (FICImageTable *imageTable in [_imageTables allValues]) {
478 | dispatch_async([[self class] dispatchQueue], ^{
479 | [imageTable reset];
480 | });
481 | }
482 | }
483 |
484 | #pragma mark - Logging Errors
485 |
486 | - (void)_logMessage:(NSString *)message {
487 | if (_delegateImplementsErrorDidOccurWithMessage) {
488 | [_delegate imageCache:self errorDidOccurWithMessage:message];
489 | }
490 | }
491 |
492 | @end
493 |
--------------------------------------------------------------------------------
/FastImageCache/FastImageCache/FastImageCache/FICImageFormat.h:
--------------------------------------------------------------------------------
1 | //
2 | // FICImageFormat.h
3 | // FastImageCache
4 | //
5 | // Copyright (c) 2013 Path, Inc.
6 | // See LICENSE for full license agreement.
7 | //
8 |
9 | #import "FICImports.h"
10 |
11 | @class FICImageTable;
12 |
13 | typedef NS_OPTIONS(NSUInteger, FICImageFormatDevices) {
14 | FICImageFormatDevicePhone = 1 << UIUserInterfaceIdiomPhone,
15 | FICImageFormatDevicePad = 1 << UIUserInterfaceIdiomPad,
16 | };
17 |
18 | typedef NS_ENUM(NSUInteger, FICImageFormatStyle) {
19 | FICImageFormatStyle32BitBGRA,
20 | FICImageFormatStyle32BitBGR,
21 | FICImageFormatStyle16BitBGR,
22 | FICImageFormatStyle8BitGrayscale,
23 | };
24 |
25 | typedef NS_ENUM(NSUInteger, FICImageFormatProtectionMode) {
26 | FICImageFormatProtectionModeNone,
27 | FICImageFormatProtectionModeComplete,
28 | FICImageFormatProtectionModeCompleteUntilFirstUserAuthentication,
29 | };
30 |
31 | /**
32 | `FICImageFormat` acts as a definition for the types of images that are stored in the image cache. Each image format must have a unique name, but multiple formats can belong to the same family.
33 | All images associated with a particular format must have the same image dimentions and opacity preference. You can define the maximum number of entries that an image format can accommodate to
34 | prevent the image cache from consuming too much disk space. Each `` managed by the image cache is associated with a single image format.
35 | */
36 |
37 | NS_ASSUME_NONNULL_BEGIN
38 | @interface FICImageFormat : NSObject
39 |
40 | ///------------------------------
41 | /// @name Image Format Properties
42 | ///------------------------------
43 |
44 | /**
45 | The name of the image format. Each image format must have a unique name.
46 |
47 | @note Since multiple instances of Fast Image Cache can exist in the same application, it is important that image format name's be unique across all instances of ``. Reverse DNS naming
48 | is recommended (e.g., com.path.PTUserProfilePhotoLargeImageFormat).
49 | */
50 | @property (nonatomic, copy) NSString *name;
51 |
52 | /**
53 | The optional family that the image format belongs to. Families group together related image formats.
54 |
55 | @discussion If you are using the image cache to create several different cached variants of the same source image, all of those variants would be unique image formats that share the same family.
56 |
57 | For example, you might define a `userPhoto` family that groups together image formats with the following names: `userPhotoSmallThumbnail`, `userPhotoLargeThumbnail`, `userPhotoLargeThumbnailBorder`.
58 | Ideally, the same source image can be processed to create cached image data for every image format belonging to the same family.
59 |
60 | `` provides its delegate a chance to process all image formats in a given family at the same time when a particular entity-image format pair is being processed. This allows you to process
61 | a source image once instead of having to download and process the same source image multiple times for different formats in the same family.
62 |
63 | @see [FICImageCacheDelegate imageCache:shouldProcessAllFormatsInFamily:forEntity:]
64 | */
65 | @property (nonatomic, copy) NSString *family;
66 |
67 | /**
68 | The size, in points, of the images stored in the image table created by this format.
69 | */
70 | @property (nonatomic, assign) CGSize imageSize;
71 |
72 | /**
73 | A bitmask of type `` that defines the style of the image format.
74 |
75 | `FICImageFormatStyle` has the following values:
76 |
77 | - `FICImageFormatStyle32BitBGRA`: Full-color image format with alpha channel. 8 bits per color component, and 8 bits for the alpha channel.
78 | - `FICImageFormatStyle32BitBGR`: Full-color image format with no alpha channel. 8 bits per color component. The remaining 8 bits are unused.
79 | - `FICImageFormatStyle16BitBGR`: Reduced-color image format with no alpha channel. 5 bits per color component. The remaining bit is unused.
80 | - `FICImageFormatStyle8BitGrayscale`: Grayscale-only image format with no alpha channel.
81 |
82 | If you are storing images without an alpha component (e.g., JPEG images), then you should use the `FICImageFormatStyle32BitBGR` style for performance reasons. If you are storing very small images or images
83 | without a great deal of color complexity, the `FICImageFormatStyle16BitBGR` style may be sufficient and uses less disk space than the 32-bit styles use. For grayscale-only image formats, the
84 | `FICImageFormatStyle8BitGrayscale` style is sufficient and further reduces disk space usage.
85 | */
86 | @property (nonatomic, assign) FICImageFormatStyle style;
87 |
88 | /**
89 | The maximum number of entries that an image table can contain for this image format.
90 |
91 | @discussion Images inserted into the image table defined by this image format after the maximum number of entries has been exceeded will replace the least-recently accessed entry.
92 | */
93 | @property (nonatomic, assign) NSInteger maximumCount;
94 |
95 | /**
96 | A bitmask of type `` that defines which devices are managed by an image table.
97 |
98 | @discussion If the current device is not included in a particular image format, the image cache will not store image data for that device.
99 | */
100 | @property (nonatomic, assign) FICImageFormatDevices devices;
101 |
102 | /**
103 | The size, in pixels, of the images stored in the image table created by this format. This takes into account the screen scale.
104 | */
105 | @property (nonatomic, assign, readonly) CGSize pixelSize;
106 |
107 | /**
108 | The bitmap info associated with the images created with this image format.
109 | */
110 | @property (nonatomic, assign, readonly) CGBitmapInfo bitmapInfo;
111 |
112 | /**
113 | The number of bytes each pixel of an image created with this image format occupies.
114 | */
115 | @property (nonatomic, assign, readonly) NSInteger bytesPerPixel;
116 |
117 | /**
118 | The number of bits each pixel component (e.g., blue, green, red color channels) uses for images created with this image format.
119 | */
120 | @property (nonatomic, assign, readonly) NSInteger bitsPerComponent;
121 |
122 | /**
123 | Whether or not the the images represented by this image format are grayscale.
124 | */
125 | @property (nonatomic, assign, readonly) BOOL isGrayscale;
126 |
127 | /**
128 | The data protection mode that image table files will be created with.
129 |
130 | `FICImageFormatProtectionMode` has the following values:
131 |
132 | - `FICImageFormatProtectionModeNone`: No data protection is used. The image table file backing this image format will always be available for reading and writing.
133 | - `FICImageFormatProtectionModeComplete`: Complete data protection is used. As soon as the system enables data protection (i.e., when the device is locked), the image table file backing this image
134 | format will not be available for reading and writing. As a result, images of this format should not be requested by Fast Image Cache when executing backgrounded code.
135 | - `FICImageFormatProtectionModeCompleteUntilFirstUserAuthentication`: Partial data protection is used. After a device restart, until the user unlocks the device for the first time, complete data
136 | protection is in effect. However, after the device has been unlocked for the first time, the image table file backing this image format will remain available for readin and writing. This mode may be
137 | a good compromise between encrypting image table files after the device powers down and allowing the files to be accessed successfully by Fast Image Cache, whether or not the device is subsequently
138 | locked.
139 |
140 | @note Data protection can prevent Fast Image Cache from accessing its image table files to read and write image data. If the image data being stored in Fast Image Cache is not sensitive in nature,
141 | consider using `FICImageFormatProtectionModeNone` to prevent any issues accessing image table files when the disk is encrypted.
142 | */
143 | @property (nonatomic, assign) FICImageFormatProtectionMode protectionMode;
144 |
145 | /**
146 | The string representation of ``.
147 | */
148 | @property (nonatomic, assign, readonly) NSString *protectionModeString;
149 |
150 | /**
151 | The dictionary representation of this image format.
152 |
153 | @discussion Fast Image Cache automatically serializes the image formats that it uses to disk. If an image format ever changes, Fast Image Cache automatically detects the change and invalidates the
154 | image table associated with that image format. The image table is then recreated from the updated image format.
155 | */
156 | @property (nonatomic, copy, readonly) NSDictionary *dictionaryRepresentation;
157 |
158 | ///-----------------------------------
159 | /// @name Initializing an Image Format
160 | ///-----------------------------------
161 |
162 | /**
163 | Convenience initializer to create a new image format.
164 |
165 | @param name The name of the image format. Each image format must have a unique name.
166 |
167 | @param family The optional family that the image format belongs to. See the `` property description for more information.
168 |
169 | @param imageSize The size, in points, of the images stored in the image table created by this format.
170 |
171 | @param style The style of the image format. See the `